robotarium / robotarium_python_simulator

A Python simulator for the Robotarium! See your algorithm run on real robots!
MIT License
111 stars 59 forks source link

Simulator Visualization Performance Degrades Over Time #5

Open guyfleeman opened 5 years ago

guyfleeman commented 5 years ago

Overtime, simulator render performance degrades. This is particularly noticeable if you set the update time to something like 1/10 of a second. The simulator initially makes this interval, but after a few seconds begins failing to render an update in 1/10 of second. Performance degrades continuously.

I'm testing on formation_control with and update_time of (-1) which renders an update every step. I've profiled the program and found the culprit. The runtime is 56 seconds. Almost all of that is spent in rendering.

0.026    0.000   49.057    0.025 backend_tkagg.py:303(draw)
0.031    0.000   39.363    0.020 backend_agg.py:418(draw)
0.465    0.000   34.552    0.017 artist.py:47(draw_wrapper)
0.052    0.000   32.219    0.016 image.py:120(_draw_list_compositing_images)
0.082    0.000   24.152    0.006 axis.py:1125(draw)

The above trace is stripped from the full cPython trace. The root issue here is _draw_list_compositing_images, with axis.py helping to illuminate the issue. The sim shoudn't be compositing images every update, it really shouldn't be compositing images AT ALL after the initial render. Compositing is extremely expensive, as it has no provision to constrain operations to only areas of the frame that need updating. The _draw_list_compositing_images function (or somewhere in its call stack) appears to need to allocate intermediate compositing buffers everytime it's called. Over time the heap is thrashed, and allocation/gc cycles become more expensive causing the degradation. This eventually plateaus once the heap distribution is the approximately worst case.

Matplotlib has two render methods designed for rapid updated: One is a nice animation callback system, but this would force us to use locks to safely pass data from the main thread (sim) to the callback animation handler. I think, for now, this complexity is premature.

Two is to manually define the render loop to use artist blitting which is GL/Mesa accelerated to mask only update regions and then blit (filtered accelerated composite) the artist region onto the canvas. In fact, the canvas is never redrawn. You don't want to redraw the entire canvas everytime because of the compositing issue. The background (axes and stuff) is composited once before the plot is shown, and cached as the background image. Then the artists are blitted on top. Using the method, people have show fps target of 200-400 fps for multi-plot graph updates. We can see from the cPython trace almost half the time was spent re-compositing the axes. Once cached this will occur once before the window is even visible and before the first step.

I'm going to work on approach two as the architectural changes are pretty minor and the possible improvement is significant. I hope to have a patch in a few days.

pglotfel commented 5 years ago

Yeah, so this issue has come up before, but I haven't had time to look into the blit-ing. If you'd like to tackle this issue, the fix would be more than welcome.