diff -r 11df9d67d44b doc/tutorial/source/conceptual-overview.rst --- a/doc/tutorial/source/conceptual-overview.rst Sat Apr 06 17:54:20 2013 +0200 +++ b/doc/tutorial/source/conceptual-overview.rst Sat Apr 06 21:09:45 2013 +0200 @@ -313,7 +313,7 @@ ``Debugging`` book and then select the ``Logging`` page. You should now be looking at the Doxygen documentation for the Logging module. -In the list of ``#define``'s at the top of the page you will see the entry +In the list of ``#define's at the top of the page you will see the entry for ``NS_LOG_COMPONENT_DEFINE``. Before jumping in, it would probably be good to look for the "Detailed Description" of the logging module to get a feel for the overall operation. You can either scroll down or select the @@ -340,6 +340,21 @@ the first function run. There is nothing at all special here. Your |ns3| script is just a C++ program. +The next line sets the time resolution to one nanosecond, which happens +to be the default value: + +:: + + Time::SetResolution (Time::NS); + +The resolution is the smallest time value that can be represented (as well as +the smallest representable difference between two time values). +You can change the resolution exactly once. The mechanism enabling this +flexibility is somewhat memory hungry, so once the resolution has been +set explicitly we release the memory, preventing further updates. (If +you don't set the resolution explicitly, it will default to one nanosecond, +and the memory will be released when the simulation starts.) + The next two lines of the script are used to enable two logging components that are built into the Echo Client and Echo Server applications: diff -r 11df9d67d44b examples/tutorial/first.cc --- a/examples/tutorial/first.cc Sat Apr 06 17:54:20 2013 +0200 +++ b/examples/tutorial/first.cc Sat Apr 06 21:09:45 2013 +0200 @@ -27,6 +27,7 @@ int main (int argc, char *argv[]) { + Time::SetResolution (Time::NS); LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO); LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO); diff -r 11df9d67d44b src/core/model/nstime.h --- a/src/core/model/nstime.h Sat Apr 06 17:54:20 2013 +0200 +++ b/src/core/model/nstime.h Sat Apr 06 21:09:45 2013 +0200 @@ -26,125 +26,64 @@ #include "int64x64.h" #include #include +#include #include namespace ns3 { +// Forward declaration +class Simulator; + /** * \ingroup core * \defgroup time Time */ /** * \ingroup time - * \brief keep track of time unit. + * \brief Keep track of time values and allow control of global simulation resolution. * - * This template class is used to keep track of the value - * of a specific time unit: the type TimeUnit<1> is used to - * keep track of seconds, the type TimeUnit<2> is used to keep - * track of seconds squared, the type TimeUnit<-1> is used to - * keep track of 1/seconds, etc. - * - * This base class defines all the functionality shared by all - * these time unit objects: it defines all the classic arithmetic - * operators +, -, *, /, and all the classic comparison operators: + * This class defines the classic addition/subtraction C++ arithmetic + * operators +, -, +=, -=, and all the classic comparison operators: * ==, !=, <, >, <=, >=. It is thus easy to add, substract, or - * multiply multiple TimeUnit objects. The return type of any such - * arithmetic expression is always a TimeUnit object. - * - * The ns3::uint64_t, ns3::Time, ns3::TimeSquare, and ns3::TimeInvert classes - * are aliases for the TimeUnit<0>, TimeUnit<1>, TimeUnit<2> and TimeUnit<-1> - * types respectively. - * - * For example: - * \code - * Time<1> t1 = Seconds (10.0); - * Time<1> t2 = Seconds (10.0); - * Time<2> t3 = t1 * t2; - * Time<0> t4 = t1 / t2; - * Time<3> t5 = t3 * t1; - * Time<-2> t6 = t1 / t5; - * TimeSquare t7 = t3; - * uint64_t s = t4; - * \endcode - * - * If you try to assign the result of an expression which does not - * match the type of the variable it is assigned to, you will get a - * compiler error. For example, the following will not compile: - * \code - * Time<1> = Seconds (10.0) * Seconds (1.5); - * \endcode - * - * You can also use the following non-member functions to manipulate - * any of these ns3::TimeUnit object: - * - \ref ns3-Time-Abs ns3::Abs - * - \ref ns3-Time-Max ns3::Max - * - \ref ns3-Time-Min ns3::Min - */ -/** - * \ingroup time - * \brief keep track of time values and allow control of global simulation resolution - * - * This class defines all the classic C++ arithmetic - * operators +, -, *, /, and all the classic comparison operators: - * ==, !=, <, >, <=, >=. It is thus easy to add, substract, or - * multiply multiple Time objects. - * - * The ns3::uint64_t, ns3::TimeSquare, and ns3::TimeInvert classes - * are backward-compatibility aliases for ns3::Time. + * compare Time objects. * * For example: * \code * Time t1 = Seconds (10.0); * Time t2 = Seconds (10.0); - * Time t3 = t1 * t2; - * Time t4 = t1 / t2; - * Time t5 = t3 * t1; - * Time t6 = t1 / t5; - * Time t7 = t3; + * Time t3 = t1; + * t3 += t2; * \endcode * * You can also use the following non-member functions to manipulate * any of these ns3::Time object: - * - \ref ns3-Time-Abs ns3::Abs - * - \ref ns3-Time-Max ns3::Max - * - \ref ns3-Time-Min ns3::Min + * - \ref Abs() + * - \ref Max() + * - \ref Min() * - * This class also controls - * the resolution of the underlying time value . The default resolution - * is nanoseconds. That is, TimeStep (1).GetNanoSeconds () will return - * 1. It is possible to either increase or decrease the resolution and the - * code tries really hard to make this easy. + * This class also controls the resolution of the underlying time value. + * The resolution is the smallest representable time interval. + * The default resolution is nanoseconds. * - * If your resolution is X (say, nanoseconds) and if you create Time objects - * with a lower resolution (say, picoseconds), don't expect that this - * code will return 1: PicoSeconds (1).GetPicoSeconds (). It will most - * likely return 0 because the Time object has only 64 bits of fractional - * precision which means that PicoSeconds (1) is stored as a 64-bit aproximation - * of 1/1000 in the Time object. If you later multiply it again by the exact - * value 1000, the result is unlikely to be 1 exactly. It will be close to - * 1 but not exactly 1. - * - * In general, it is thus a really bad idea to try to use time objects of a - * resolution higher than the global resolution controlled through - * Time::SetResolution. If you do need to use picoseconds, it's thus best - * to switch the global resolution to picoseconds to avoid nasty surprises. + * To change the resolution, use SetResolution(). All Time objects created + * before the call to SetResolution() will be updated to the new resolution. + * This can only be done once! (Tracking each Time object uses 4 pointers. + * For speed, once we convert the existing instances we discard the recording + * data structure and stop tracking new instances, so we have no way + * to do a second conversion.) * - * Another important issue to keep in mind is that if you increase the - * global resolution, you also implicitely decrease the range of your simulation. - * i.e., the global simulation time is stored in a 64 bit integer whose interpretation - * will depend on the global resolution so, 2^64 picoseconds which is the maximum - * duration of your simulation if the global resolution is picoseconds - * is smaller than 2^64 nanoseconds which is the maximum duration of your simulation - * if the global resolution is nanoseconds. + * Because of the memory (and modest construction cost) of tracking Time + * objects, simulations should explicitly choose a resolution before + * calling Simulator::Run (). If you don't set the resolution explicitly, + * it will default to one nanosecond, and the memory will be released + * when the simulation starts. * - * Finally, don't even think about ever changing the global resolution after - * creating Time objects: all Time objects created before the call to SetResolution - * will contain values which are not updated to the new resolution. In practice, - * the default value for the attributes of many models is indeed calculated - * before the main function of the main program enters. Because of this, if you - * use one of these models (and it's likely), it's going to be hard to change - * the global simulation resolution in a way which gives reasonable results. This - * issue has been filed as bug 954 in the ns-3 bugzilla installation. + * If you increase the global resolution, you also implicitly decrease + * the range of your simulation. The global simulation time is stored + * in a 64 bit integer, whose interpretation will depend on the global + * resolution. Therefore the maximum duration of your simulation, + * if you use picoseconds, is 2^64 ps = 2^24 s = 7 months, whereas, + * had you used nanoseconds, you could have run for 584 years. */ class Time { @@ -154,12 +93,12 @@ */ enum Unit { - S = 0, - MS = 1, - US = 2, - NS = 3, - PS = 4, - FS = 5, + S = 0, //!< second + MS = 1, //!< millisecond + US = 2, //!< microsecond + NS = 3, //!< nanosecond + PS = 4, //!< picosecond + FS = 5, //!< femtosecond LAST = 6 }; @@ -168,44 +107,93 @@ m_data = o.m_data; return *this; } + inline Time &operator = (const int64_t &value) + { + m_data = value; + return *this; + } inline Time () : m_data () - {} + { + if (g_markingTimes) + { + Mark (this); + } + } inline Time(const Time &o) : m_data (o.m_data) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } explicit inline Time (double v) : m_data (lround (v)) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } explicit inline Time (int v) : m_data (v) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } explicit inline Time (long int v) : m_data (v) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } explicit inline Time (long long int v) : m_data (v) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } explicit inline Time (unsigned int v) : m_data (v) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } explicit inline Time (unsigned long int v) : m_data (v) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } explicit inline Time (unsigned long long int v) : m_data (v) - {} - + { + if (g_markingTimes) + { + Mark (this); + } + } /** - * \brief String constructor - * Construct Time object from common time expressions like " - * 1ms" or "10s". Supported units include: - * - s (seconds) - * - ms (milliseconds) - * - us (microseconds) - * - ns (nanoseconds) - * - ps (picoseconds) - * - fs (femtoseconds) + * \brief Construct Time object from common time expressions like "1ms" + * + * Supported units include: + * - `s` (seconds) + * - `ms` (milliseconds) + * - `us` (microseconds) + * - `ns` (nanoseconds) + * - `ps` (picoseconds) + * - `fs` (femtoseconds) * * There can be no white space between the numerical portion * and the units. Any otherwise malformed string causes a fatal error to @@ -215,6 +203,17 @@ explicit Time (const std::string & s); /** + * Destructor + */ + ~Time () + { + if (g_markingTimes) + { + Clear (this); + } + } + + /** * \return true if the time is zero, false otherwise. */ inline bool IsZero (void) const @@ -249,7 +248,9 @@ { return m_data > 0; } - + /** + * \return -1,0,+1 if `this < o`, `this == o`, or `this > o` + */ inline int Compare (const Time &o) const { return (m_data < o.m_data) ? -1 : (m_data == o.m_data) ? 0 : 1; @@ -305,8 +306,7 @@ return ToInteger (Time::FS); } /** - * \returns an approximation of the time stored in this - * instance in the units specified in m_tsPrecision. + * \returns the raw time value, in the current units */ inline int64_t GetTimeStep (void) const { @@ -436,30 +436,41 @@ } explicit inline Time (const int64x64_t &value) : m_data (value.GetHigh ()) - {} + { + if (g_markingTimes) + { + Mark (this); + } + } inline static Time From (const int64x64_t &value) { return Time (value); } private: + /** + * How to convert between other units and the current unit + */ struct Information { - bool toMul; - bool fromMul; - uint64_t factor; - int64x64_t timeTo; - int64x64_t timeFrom; + bool toMul; //!< Multiply when converting To, otherwise divide + bool fromMul; //!< Multiple when converting From, otherwise divide + uint64_t factor; //!< Ratio of this unit / current unit + int64x64_t timeTo; //!< Multiplier to convert to this unit + int64x64_t timeFrom; //!< Multiplier to convert from this unit }; + /** + * Current time unit, and conversion info. + */ struct Resolution { - struct Information info[LAST]; - enum Time::Unit unit; + struct Information info[LAST]; //!< Conversion info from current unit + enum Time::Unit unit; //!< Current time unit }; static inline struct Resolution *PeekResolution (void) { - static struct Time::Resolution resolution = GetNsResolution (); + static struct Time::Resolution resolution = SetDefaultNsResolution (); return &resolution; } static inline struct Information *PeekInformation (enum Unit timeUnit) @@ -467,8 +478,72 @@ return &(PeekResolution ()->info[timeUnit]); } - static struct Resolution GetNsResolution (void); - static void SetResolution (enum Unit unit, struct Resolution *resolution); + static struct Resolution SetDefaultNsResolution (void); + static void SetResolution (enum Unit unit, struct Resolution *resolution, + const bool convert = true); + + /** + * Record all instances of Time, so we can rescale them when + * the resolution changes. + * + * \intern + * + * We use a std::set so we can remove the record easily when + * ~Time() is called. + * + * We don't use Ptr