Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(python): use deque for insert-order message queue (#1316)
### Changelog <!-- Write a one-sentence summary of the user-impacting change (API, UI/UX, performance, etc) that could appear in a changelog. Write "None" if there is no user-facing change --> Significantly increases the performance of iterating through an MCAP file in insertion order. ### Docs <!-- Link to a Docs PR, tracking ticket in Linear, OR write "None" if no documentation changes are needed. --> None. (internal only) ### Description <!-- Describe the problem, what has changed, and motivation behind those changes. Pretend you are advocating for this change and the reader is skeptical. --> I used `iter_messages(log_time_order=False)` since my application doesn't care about message order, expecting it to perform better than `log_time_order=True` (the default). However I noticed that my application took _17 minutes_ to iterate through 3.5 million MCAP messages. It took 32 seconds with `log_time_order=True`. The previous message queue implementation used a `list` as a FIFO queue, which has poor performance as `pop(0)` will copy the list every time an item is evicted. After changing the implementation to use a `deque`, `log_time_order=False` took 15 seconds. <!-- In addition to unit tests, describe any manual testing you did to validate this change. --> Here's code for a minimal performance test. Note that the mcap file has ~3.5 million messages. ```python import time from collections import deque from mcap.reader import make_reader with open("example.mcap", "rb") as f: reader = make_reader(f) start = time.time() deque(reader.iter_messages(log_time_order=False), maxlen=0) end = time.time() print(end - start) ``` Note that `deque(iterable, maxlen=0)` will consume the iterable in C, so basically as fast as possible (see the `itertools` [recipe](https://docs.python.org/3/library/itertools.html#itertools-recipes) for `consume()`). I ran this three times after the fix, but just once before because I didn't have the patience 😄 <table><tr><th>Before</th><th>After</th></tr><tr><td> 1125.6354639530182 </td><td> 11.093337059020996</br> 10.840037822723389</br> 10.490556001663208 </td></tr></table> Over 100x faster! If you're curious, `log_time_order=True` was ~17 seconds (both before and after the changeset). <!-- If necessary, link relevant Linear or Github issues. Use `Fixes: foxglove/repo#1234` to auto-close the Github issue or Fixes: FG-### for Linear isses. --> I also updated the code to have two separate impls rather than a bifurcated implementation. I added a unit test to assert that `log_time_order=False` is faster than `True` (purposely adding messages in reverse log time order so that the heap should perform worse). As with most perf unit tests, it is potentially flakey with smaller inputs so I put 10k messages in it, but feel free to discuss if there's a different way you want to run through that.
- Loading branch information