systemc: use list instead of map in scheduler
authorEarl Ou <shunhsingou@google.com>
Tue, 15 Sep 2020 05:07:25 +0000 (13:07 +0800)
committerEarl Ou <shunhsingou@google.com>
Thu, 17 Sep 2020 02:26:05 +0000 (02:26 +0000)
The queue in systemC scheduler is implemented as a std::map. This provides
the best big-O solution. However, most of simulation usecases has very
small number of pending events. This is expected as we usually only trigger a
few new events after some events are processed. In such scenario, we
should optimize for insert/erase instead of search. This change use
std::list instead of std::map.

As a proof, we can find that gem5's original event_queue is also
implemented as a list instead of tree.

We see 5% speed improvement with the example provided by Matthias Jung:
https://gist.github.com/myzinsky/557200aa04556de44a317e0a10f51840

Change-Id: I75c30df9134e94df42fd778115cf923488ff5886
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/34515
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
src/systemc/core/scheduler.cc
src/systemc/core/scheduler.hh

index 179bd5523dc033f889af3f0c91e6cbc2c864eaee..50a1e6bd3449741fd1f7bcd895f83558c7f20bd4 100644 (file)
@@ -71,8 +71,7 @@ Scheduler::clear()
         deltas.front()->deschedule();
 
     // Timed notifications.
-    for (auto &tsp: timeSlots) {
-        TimeSlot *&ts = tsp.second;
+    for (auto &ts: timeSlots) {
         while (!ts->events.empty())
             ts->events.front()->deschedule();
         deschedule(ts);
index c9ca161cf17fe5a44f145f49fbbefdec17127ace..693cb3a848742e6047c674d90df7c7544f9b4cba 100644 (file)
@@ -29,6 +29,7 @@
 #define __SYSTEMC_CORE_SCHEDULER_HH__
 
 #include <functional>
+#include <list>
 #include <map>
 #include <mutex>
 #include <set>
@@ -151,13 +152,17 @@ class Scheduler
     class TimeSlot : public ::Event
     {
       public:
-        TimeSlot() : ::Event(Default_Pri, AutoDelete) {}
-
+        TimeSlot(const Tick& targeted_when) : ::Event(Default_Pri, AutoDelete),
+                                              targeted_when(targeted_when) {}
+        // Event::when() is only set after it's scheduled to an event queue.
+        // However, TimeSlot won't be scheduled before init is done. We need
+        // to keep the real 'targeted_when' information before scheduled.
+        Tick targeted_when;
         ScEvents events;
         void process();
     };
 
-    typedef std::map<Tick, TimeSlot *> TimeSlots;
+    typedef std::list<TimeSlot *> TimeSlots;
 
     Scheduler();
     ~Scheduler();
@@ -250,12 +255,14 @@ class Scheduler
         }
 
         // Timed notification/timeout.
-        TimeSlot *&ts = timeSlots[tick];
-        if (!ts) {
-            ts = new TimeSlot;
-            schedule(ts, tick);
+        auto it = timeSlots.begin();
+        while (it != timeSlots.end() && (*it)->targeted_when < tick)
+            it++;
+        if (it == timeSlots.end() || (*it)->targeted_when != tick) {
+            it = timeSlots.emplace(it, new TimeSlot(tick));
+            schedule(*it, tick);
         }
-        event->schedule(ts->events, tick);
+        event->schedule((*it)->events, tick);
     }
 
     // For descheduling delayed/timed notifications/timeouts.
@@ -270,10 +277,15 @@ class Scheduler
         }
 
         // Timed notification/timeout.
-        auto tsit = timeSlots.find(event->when());
-        panic_if(tsit == timeSlots.end(),
+        auto tsit = timeSlots.begin();
+        while (tsit != timeSlots.end() &&
+               (*tsit)->targeted_when < event->when())
+            tsit++;
+
+        panic_if(tsit == timeSlots.end() ||
+                 (*tsit)->targeted_when != event->when(),
                 "Descheduling event at time with no events.");
-        TimeSlot *ts = tsit->second;
+        TimeSlot *ts = *tsit;
         ScEvents &events = ts->events;
         assert(on == &events);
         event->deschedule();
@@ -288,7 +300,7 @@ class Scheduler
     void
     completeTimeSlot(TimeSlot *ts)
     {
-        assert(ts == timeSlots.begin()->second);
+        assert(ts == timeSlots.front());
         timeSlots.erase(timeSlots.begin());
         if (!runToTime && starved())
             scheduleStarvationEvent();
@@ -324,7 +336,7 @@ class Scheduler
         if (pendingCurr())
             return 0;
         if (pendingFuture())
-            return timeSlots.begin()->first - getCurTick();
+            return timeSlots.front()->targeted_when - getCurTick();
         return MaxTick - getCurTick();
     }
 
@@ -434,7 +446,8 @@ class Scheduler
     {
         return (readyListMethods.empty() && readyListThreads.empty() &&
                 updateList.empty() && deltas.empty() &&
-                (timeSlots.empty() || timeSlots.begin()->first > maxTick) &&
+                (timeSlots.empty() ||
+                 timeSlots.front()->targeted_when > maxTick) &&
                 initList.empty());
     }
     EventWrapper<Scheduler, &Scheduler::pause> starvationEvent;