systemc: use list instead of map in scheduler

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>
This commit is contained in:
Earl Ou
2020-09-15 13:07:25 +08:00
parent 9a39ac876e
commit 1bca30c95b
2 changed files with 28 additions and 16 deletions

View 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);

View 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;