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.
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "calendar-scheduler.h"
|
||||
#include "event-impl.h"
|
||||
#include "type-id.h"
|
||||
#include "boolean.h"
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <list>
|
||||
@@ -46,6 +48,12 @@ CalendarScheduler::GetTypeId (void)
|
||||
.SetParent<Scheduler> ()
|
||||
.SetGroupName ("Core")
|
||||
.AddConstructor<CalendarScheduler> ()
|
||||
.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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ private:
|
||||
Ptr<RandomVariableStream> 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);
|
||||
|
||||
Reference in New Issue
Block a user