From c9769dc4002134063cf0cb3d6210b1a37d9fc8a2 Mon Sep 17 00:00:00 2001 From: "Peter D. Barnes, Jr" Date: Fri, 20 Mar 2020 17:37:38 -0700 Subject: [PATCH] core: refactor reverse ordering in calendar scheduler Refactor to clarify the core algorithms and isolate the forward/reverse differences to one section. This also lets the insertion order to be set by Attribute. Benchmarking this appears to be marginally faster than Alexander Krotov's original version. It's not clear why; should be marginally slower. --- src/core/model/calendar-scheduler.cc | 66 +++++++++++++++++++++------- src/core/model/calendar-scheduler.h | 64 ++++++++++++++++++++++----- utils/bench-simulator.cc | 12 ++++- 3 files changed, 114 insertions(+), 28 deletions(-) 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);