瀏覽代碼

Event queue: more efficient removing of items

Joeri Exelmans 5 年之前
父節點
當前提交
4deb813733
共有 3 個文件被更改,包括 21 次插入21 次删除
  1. 1 2
      src/sccd/controller/controller.py
  2. 20 17
      src/sccd/controller/event_queue.py
  3. 0 2
      src/sccd/realtime/eventloop.py

+ 1 - 2
src/sccd/controller/controller.py

@@ -34,8 +34,7 @@ class Controller:
 
         def schedule_after(after, event, instances):
             entry = Controller.EventQueueEntry(event, instances)
-            self.queue.add(self.simulated_time + after, entry)
-            return entry
+            return self.queue.add(self.simulated_time + after, entry)
 
         def cancel_after(entry):
             self.queue.remove(entry)

+ 20 - 17
src/sccd/controller/event_queue.py

@@ -10,19 +10,26 @@ Item = TypeVar('Item')
 class EventQueue(Generic[Timestamp, Item]):
     __slots__ = ["queue", "counters", "removed"]
 
+    # We don't define our own event queue item class here
+    # with __lt__ because tuples are faster to compare.
+    # Tuples are however immutable, so we "wrap" the 'removed'
+    # flag in an object:
+    class RemovedWrapper:
+        __slots__ = ["removed"]
+        def __init__(self):
+            self.removed = False
+
     def __init__(self):
-        self.queue: List[Tuple[Timestamp, int, Item]] = []
+        self.queue: List[Tuple[Timestamp, int, RemovedWrapper, Item]] = []
         self.counters = {} # mapping from timestamp to number of items at timestamp
-        self.removed: Set[Item] = set()
 
     def __str__(self):
-        return str(sorted([tup for tup in self.queue if tup[2] not in self.removed]))
+        return str(sorted([tup for tup in self.queue if not tup[2].removed]))
 
     def earliest_timestamp(self) -> Optional[Timestamp]:
         with timer.Context("event_queue"):
-            while self.queue and (self.queue[0][2] in self.removed):
-                tup = heappop(self.queue)
-                self.removed.remove(tup[2])
+            while self.queue and self.queue[0][2].removed:
+                heappop(self.queue)
             try:
                 return self.queue[0][0]
             except IndexError:
@@ -31,28 +38,24 @@ class EventQueue(Generic[Timestamp, Item]):
     def add(self, timestamp: Timestamp, item: Item):
         # print("add", item)
         with timer.Context("event_queue"):
-            self.counters[timestamp] = self.counters.setdefault(timestamp, 0) + 1
-            def_event = (timestamp, self.counters[timestamp], item)
+            n = self.counters[timestamp] = self.counters.setdefault(timestamp, 0) + 1
+            def_event = (timestamp, n, EventQueue.RemovedWrapper(), item)
             heappush(self.queue, def_event)
+            return def_event
 
-    def remove(self, item: Item):
+    def remove(self, item: Tuple[Timestamp, int, RemovedWrapper, Item]):
         # print("remove", item)
         with timer.Context("event_queue"):
-            self.removed.add(item)
-            if len(self.removed) > 100:
-                self.queue = [x for x in self.queue if x[2] not in self.removed]
-                # print("heapify")
-                heapify(self.queue)
-                self.removed.clear()
+            item[2].removed = True
 
     # Raises exception if called on empty queue
     def pop(self) -> Tuple[Timestamp, Item]:
         with timer.Context("event_queue"):
             while 1:
-                timestamp, n, item = heappop(self.queue)
+                timestamp, n, removed, item = heappop(self.queue)
                 if self.counters[timestamp] == n:
                     del self.counters[timestamp]
-                if item not in self.removed:
+                if not removed.removed:
                     return (timestamp, item)
 
     def is_due(self, timestamp: Optional[Timestamp]) -> bool:

+ 0 - 2
src/sccd/realtime/eventloop.py

@@ -41,8 +41,6 @@ class EventLoop:
         now = self.timer.now()
         next_wakeup = self.controller.next_wakeup()
         if next_wakeup:
-            # (next_wakeup - now) is negative, we are running behind
-            # not much we can do about it though
             sleep_duration = self.to_event_loop_unit(next_wakeup - now)
             if sleep_duration < 0:
                 self.purposefully_behind = now - next_wakeup