diff --git a/src/core/model/calendar-scheduler.cc b/src/core/model/calendar-scheduler.cc index c98ee26d3..0fa26634a 100644 --- a/src/core/model/calendar-scheduler.cc +++ b/src/core/model/calendar-scheduler.cc @@ -21,6 +21,8 @@ #include "calendar-scheduler.h" #include "event-impl.h" +#include "type-id.h" +#include "boolean.h" #include #include #include @@ -46,6 +48,12 @@ CalendarScheduler::GetTypeId (void) .SetParent () .SetGroupName ("Core") .AddConstructor () + .AddAttribute ("Reverse", + "Store events in reverse chronological order", + TypeId::ATTR_CONSTRUCT, + BooleanValue (false), + MakeBooleanAccessor (&CalendarScheduler::SetReverse), + MakeBooleanChecker ()) ; return tid; } @@ -63,6 +71,43 @@ CalendarScheduler::~CalendarScheduler () m_buckets = 0; } void +CalendarScheduler::SetReverse (bool reverse) +{ + NS_LOG_FUNCTION (this << reverse); + m_reverse = reverse; + + if (m_reverse) + { + NextEvent = [] (Bucket & bucket) -> Scheduler::Event & + { + return bucket.back (); + }; + Order = [](const EventKey & a, const EventKey & b) -> bool + { + return a > b; + }; + Pop = [] (Bucket & bucket) -> void + { + bucket.pop_back (); + }; + } + else + { + NextEvent = [](Bucket & bucket) -> Scheduler::Event & + { + return bucket.front (); + }; + Order = [](const EventKey & a, const EventKey & b) -> bool + { + return a < b; + }; + Pop = [] (Bucket & bucket) -> void + { + bucket.pop_front (); + }; + } +} +void CalendarScheduler::Init (uint32_t nBuckets, uint64_t width, uint64_t startPrio) @@ -109,7 +154,7 @@ CalendarScheduler::DoInsert (const Event &ev) Bucket::iterator end = m_buckets[bucket].end (); for (Bucket::iterator i = m_buckets[bucket].begin (); i != end; ++i) { - if ((ev.key < i->key && !m_reverse) || (ev.key > i->key && m_reverse)) + if (Order (ev.key, i->key) ) { m_buckets[bucket].insert (i, ev); return; @@ -148,7 +193,7 @@ CalendarScheduler::PeekNext (void) const { if (!m_buckets[i].empty ()) { - Scheduler::Event next = m_reverse ? m_buckets[i].back () : m_buckets[i].front (); + Scheduler::Event next = NextEvent (m_buckets[i]); if (next.key.m_ts < bucketTop) { return next; @@ -184,13 +229,13 @@ CalendarScheduler::DoRemoveNext (void) { if (!m_buckets[i].empty ()) { - Scheduler::Event next = m_reverse ? m_buckets[i].back () : m_buckets[i].front (); + Scheduler::Event next = NextEvent (m_buckets[i]); if (next.key.m_ts < bucketTop) { m_lastBucket = i; m_lastPrio = next.key.m_ts; m_bucketTop = bucketTop; - m_reverse ? m_buckets[i].pop_back () : m_buckets[i].pop_front (); + Pop (m_buckets[i]); return next; } if (next.key < minKey) @@ -208,8 +253,8 @@ CalendarScheduler::DoRemoveNext (void) m_lastPrio = minKey.m_ts; m_lastBucket = Hash (minKey.m_ts); m_bucketTop = (minKey.m_ts / m_width + 1) * m_width; - Scheduler::Event next = m_reverse ? m_buckets[minBucket].back () : m_buckets[minBucket].front(); - m_reverse ? m_buckets[minBucket].pop_back () : m_buckets[minBucket].pop_front (); + Scheduler::Event next = NextEvent (m_buckets[minBucket]); + Pop (m_buckets[minBucket]); return next; } @@ -383,13 +428,4 @@ CalendarScheduler::Resize (uint32_t newSize) DoResize (newSize, newWidth); } -bool CalendarScheduler::SetReverse (bool reverse) -{ - NS_LOG_FUNCTION (this << reverse); - - bool old_reverse = m_reverse; - m_reverse = reverse; - return old_reverse; -} - } // namespace ns3 diff --git a/src/core/model/calendar-scheduler.h b/src/core/model/calendar-scheduler.h index 499dbc622..e647035fb 100644 --- a/src/core/model/calendar-scheduler.h +++ b/src/core/model/calendar-scheduler.h @@ -47,20 +47,31 @@ class EventImpl; * refinements published later but this class implements * the original algorithm (to the best of my knowledge). * - * \note - * This queue is much slower than I expected (much slower than the + * The default behavior is to store events in each bucket in + * increasing timestamp order. This can be changed to reverse + * timstamp order using the Attribute \c Reverse, + * + * To change the ordering use the following pattern: + * \code + * ObjectFactory factory ("ns3::CalendarScheduler"); + * factory.SetAttribute ("Reverse", BooleanValue (true)); + * Simulator::SetScheduler (factory); + * \endcode + * + * \note This queue is much slower than I expected (much slower than the * std::map queue) and this seems to be because the original resizing policy * is horribly bad. This is most likely the reason why there have been * so many variations published which all slightly tweak the resizing * heuristics to obtain a better distribution of events across buckets. * - * While inserion sort is not discussed in the original article, its + * \note While inserion sort is not discussed in the original article, its * implementation appears to dramatically affect performance. - * CalendarScheduler sorts buckets in \em reverse chronological order. - * This heuristic, originating in NS-2 implementation of - * calendar scheduler, reduces enqueue time, as it is likely that - * timestamp of new event is greater than timestamp of already - * scheduled event. + * The default implementation sorts buckets in increasing (chronological) + * order. The alternative, sorting buckets in decreasing order, + * was adopted in NS-2 because they observed that new events were + * more likely to be later than already scheduled events. + * In this case sorting buckets in reverse chronological order + * reduces enqueue time. */ class CalendarScheduler : public Scheduler { @@ -83,8 +94,6 @@ public: virtual Scheduler::Event RemoveNext (void); virtual void Remove (const Scheduler::Event &ev); - bool SetReverse (bool reverse); - private: /** Double the number of buckets if necessary. */ void ResizeUp (void); @@ -158,7 +167,40 @@ private: uint64_t m_lastPrio; /** Number of events in queue. */ uint32_t m_qSize; - /** Switch between old and new configuration after bug 2498. */ + + /** + * Set the insertion order. + * + * This can only be used at construction, as invoked by the + * Attribute Reverse. + * + * \param [in] reverse If \c true, store events in *decreasing* + * time stamp order. + */ + void SetReverse (bool reverse); + /** + * Get the next event from the bucket, according to \c m_reverse. + * \param [in] bucket The bucket to draw from. + * \return The next event from the \c bucket. + */ + Scheduler::Event & (*NextEvent) (Bucket & bucket); + /** + * Ordering function to identify the insertion point, according to \c m_reverse. + * \param [in] newEvent The new event being inserted. + * \param [in] it The current position in the bucket being examined. + * \return \c true if the \c newEvent belongs before \it. + */ + bool (*Order) (const EventKey & newEvent, const EventKey & it); + /** + * Pop the next event from the bucket, according to \c m_reverse. + * \param [in] bucket The bucket to pop from. + */ + void (*Pop) (Bucket &); + /** + * Bucket ordering. + * If \c false (default), store events in increasing time stamp order. + * If \c true, store events in *decreasing* time stamp order. + */ bool m_reverse = false; }; diff --git a/utils/bench-simulator.cc b/utils/bench-simulator.cc index 8aec6a822..fe3d5a133 100644 --- a/utils/bench-simulator.cc +++ b/utils/bench-simulator.cc @@ -91,7 +91,7 @@ private: Ptr m_rand; ///< random variable uint32_t m_population; ///< population uint32_t m_total; ///< total - uint32_t m_count; ///< count + uint32_t m_count; ///< count }; void @@ -213,6 +213,7 @@ int main (int argc, char *argv[]) uint32_t total = 1000000; uint32_t runs = 1; std::string filename = ""; + bool calRev = false; CommandLine cmd (__FILE__); cmd.Usage ("Benchmark the simulator scheduler.\n" @@ -224,6 +225,7 @@ int main (int argc, char *argv[]) "In the case of either --file form, the input is expected\n" "to be ascii, giving the relative event times in ns."); cmd.AddValue ("cal", "use CalendarSheduler", schedCal); + cmd.AddValue ("calrev", "reverse ordering in the CalendarScheduler", calRev); cmd.AddValue ("heap", "use HeapScheduler", schedHeap); cmd.AddValue ("list", "use ListSheduler", schedList); cmd.AddValue ("map", "use MapScheduler (default)", schedMap); @@ -242,6 +244,7 @@ int main (int argc, char *argv[]) if (schedCal) { factory.SetTypeId ("ns3::CalendarScheduler"); + factory.Set ("Reverse", BooleanValue (calRev)); } if (schedHeap) { @@ -261,7 +264,12 @@ int main (int argc, char *argv[]) LOGME (std::setprecision (g_fwidth - 6)); DEB ("debugging is ON"); - LOGME ("scheduler: " << factory.GetTypeId ().GetName ()); + std::string order; + if (schedCal) + { + order = ": insertion order: " + std::string (calRev ? "reverse" : "normal"); + } + LOGME ("scheduler: " << factory.GetTypeId ().GetName () << order); LOGME ("population: " << pop); LOGME ("total events: " << total); LOGME ("runs: " << runs);