-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Tick rendering speedups #29394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tick rendering speedups #29394
Conversation
How much of the speedup is due to the lru_cache()? That is something that
would impact performance all across mpl.
…On Tue, Dec 31, 2024 at 9:27 PM Scott Shambaugh ***@***.***> wrote:
PR summary
I'm starting an effort to speed up 3D rendering, spinning up a profiler on
some test cases. For this MR, I'm focusing on draw speed for an empty plot.
Based on py-spy <https://github.com/benfred/py-spy> profiling, the
largest proportion of render time is dedicated to handling tick marks. This
PR implements several optimizations to speed up tick drawing. Based on
averaging 6x runs of the below script that executes 100x draw calls, the
average render time for the basic empty plot decreased from 57 ms to 22 ms,
which is a 2.6x improvement! Qualitatively, rotating the plot definitely
feels snappier on my machine.
import matplotlib.pyplot as pltimport time
fig = plt.figure()ax = fig.add_subplot(111, projection='3d')ax.view_init(0, 0, 0)
plt.show(block=False)plt.pause(1)
# Start timingstart_time = time.perf_counter()
n = 100for i in range(n):
ax.view_init(0, 0, i*10)
plt.draw()
plt.pause(0.1)
# End timingend_time = time.perf_counter()
# Calculate actual computation time by subtracting pausestotal_pause_time = n * 0.1 # 100 iterations * 0.1 seconds per pausecomputation_time = (end_time - start_time) - total_pause_timeprint(f"Total time: {end_time - start_time:.4f} seconds")print(f"Computation time (excluding pauses): {computation_time:.4f} seconds")print(f"Average time per loop: {computation_time / n * 1000:.2f} ms")
Attached are representative before and after profiler runs, which can be
uploaded to https://www.speedscope.app/ to view
profile_ticks_speedups.speedscope.json
<https://github.com/user-attachments/files/18284234/profile_ticks_speedups.speedscope.json>
profile_ticks_base.speedscope.json
<https://github.com/user-attachments/files/18284235/profile_ticks_base.speedscope.json>
PR checklist
- "closes #0000" is in the body of the PR description to link the
related issue
<https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue>
- new and changed code is tested
<https://matplotlib.org/devdocs/devel/testing.html>
- *Plotting related* features are demonstrated in an example
<https://matplotlib.org/devdocs/devel/document.html#write-examples-and-tutorials>
- *New Features* and *API Changes* are noted with a directive and
release note
<https://matplotlib.org/devdocs/devel/api_changes.html#announce-changes-deprecations-and-new-features>
- Documentation complies with general
<https://matplotlib.org/devdocs/devel/document.html#write-rest-pages>
and docstring
<https://matplotlib.org/devdocs/devel/document.html#write-docstrings>
guidelines
------------------------------
You can view, comment on, or merge this pull request online at:
#29394
Commit Summary
- abf083b
<abf083b>
Tick rendering speedups
File Changes
(6 files <https://github.com/matplotlib/matplotlib/pull/29394/files>)
- *M* lib/matplotlib/axis.py
<https://github.com/matplotlib/matplotlib/pull/29394/files#diff-04227e6d4900298b309bddab2e848da8cc638da2913c64b5dcf0d800ba2a0c16>
(22)
- *M* lib/matplotlib/backends/backend_agg.py
<https://github.com/matplotlib/matplotlib/pull/29394/files#diff-0a415dbb618fcfb73e6191c735f6e5a91f530d4a29b8886afdfd56604892de61>
(2)
- *M* lib/matplotlib/spines.py
<https://github.com/matplotlib/matplotlib/pull/29394/files#diff-4e07c249abaaf29d0bb2bef9c5b12a86ccaa4895fcae37004bacf9d406043f8b>
(2)
- *M* lib/matplotlib/tests/test_axes.py
<https://github.com/matplotlib/matplotlib/pull/29394/files#diff-ef5144bd27a700deb760ef56ccf51dc6fcfe86723f9ffa84df7f024985bf3ee7>
(2)
- *M* lib/mpl_toolkits/mplot3d/axes3d.py
<https://github.com/matplotlib/matplotlib/pull/29394/files#diff-a67656224bfb26dc6de0a0f89e398cd6999377d1482aca3e867ee550ff05be57>
(3)
- *M* lib/mpl_toolkits/mplot3d/axis3d.py
<https://github.com/matplotlib/matplotlib/pull/29394/files#diff-ea1dd4bc7c3b7c0078e69e41e916d25354864e4f2fe31fd71210477f6f9f9660>
(36)
Patch Links:
- https://github.com/matplotlib/matplotlib/pull/29394.patch
- https://github.com/matplotlib/matplotlib/pull/29394.diff
—
Reply to this email directly, view it on GitHub
<#29394>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACHF6A7PCUXALQNDHIHBRL2ING75AVCNFSM6AAAAABUOEZ4CCVHI2DSMVQWIX3LMV43ASLTON2WKOZSG43DINZZGE4DMNQ>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
abf083b
to
059e8a5
Compare
Redid the profiling in a more stable environment and the improvement wasn't as dramatic, about 48% speedup. The |
@@ -1285,7 +1285,7 @@ def _update_ticks(self): | |||
tick.update_position(loc) | |||
tick.label1.set_text(label) | |||
tick.label2.set_text(label) | |||
ticks = [*major_ticks, *minor_ticks] | |||
self._ticks = [*major_ticks, *minor_ticks] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I‘m somewhat skeptical this is a good idea. It’s essentially a caching mechanism for the expensive (?) lookup of the current ticks. This adds more state and makes tick handling more complicated.
On a general note. The fundamental problem is the tick design:
- Ticks consist of three Line2D objects and two Text objects. This is heavy.
- Ticks are often created / modified.
This combination makes Ticks expensive. One mitigation strategy we use is that ticks are repurposed instead of being recreated - this is the update_ticks mechanism. The second trick is the lazy behavior of tick lists. Ticks are only created when you need them first. Overall these are still only bandaids on the fundamentally expensive tick design. And the change here goes into the same category.
I believe to substantially improve performance, we have to move away from heavy Tick instances and loops over then. See #5665 (comment). Alternatively/complementary, ticks could get more lightweight with a borg-like pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your assessment on Ticks being heavy, and a refactor being the real solution here. The highlighted branch of the call tree here shows the ticks taking up well over half the time associated with the figure & 3d axes creation and drawing.
Think the extra state isn't worth the ~20% speedup? I don't feel too strongly either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I‘d like to put this on hold. I plan to pick up the refactoring plan soon, and this additional state would complicate that. We might put the caching then behind the TickCollection abstraction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me!
PR summary
I'm starting an effort to speed up 3D rendering, spinning up a profiler on some test cases. For this MR, I'm focusing on draw speed for an empty plot.
Based on
py-spy
profiling, the largest proportion of render time is dedicated to handling tick marks. This PR implements several optimizations to speed up tick drawing. Based on averaging 6x runs of the below script that executes 100x draw calls, the average render time for the basic empty plot decreased from 23.1 ms to 19.1 ms, which is a 21% improvement. Qualitatively, rotating the plot definitely feels snappier on my machine.Attached are representative before and after profiler runs, which can be uploaded to https://www.speedscope.app/ to view
profile_ticks_speedups.speedscope.json
profile_ticks_base.speedscope.json
PR checklist