| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Is this safe? Are you sure they are always identical? For example constrained layout draws twice and the Axes size and limits may have changed in the second draw. Or asking the other way round: if the updates are redundant, why are we doing them in the first place? |
Sorry, something went wrong.
|
get_tightbbox() which is called during constrained_layout is flagged here to ignore the cache - beyond that I can't find anywhere that we do an update on these values in the middle of a draw cycle (and the tests aren't complaining, though I know that's not fully exculpatory). As to why we're doing them multiple times in the first place - in 3D we need to access these values in the tick drawing and the grid drawing paths, which might not both be called so both needed to potentially refresh the calcs. In 2D, I don't see a reason - my hunch it was done without realizing the performance hit of recalculating that state each time. # axis.draw()
self._clear_ticks_cache()
ticks_to_draw = self._update_ticks(_use_cache=True)
tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer, _use_cache=True)
for tick in ticks_to_draw:
tick.draw(renderer)
self._update_label_position(renderer, _use_cache=True)
self.label.draw(renderer)
self._update_offset_text_position(tlb1, tlb2)
self.offsetText.set_text(self.major.formatter.get_offset())
self.offsetText.draw(renderer)
renderer.close_group(__name__)
self._clear_ticks_cache() |
Sorry, something went wrong.
|
Actually, looking at this again, we can also cache within the get_tightbbox function. I see a similar speedup when profiling with fig.tight_layout() in the inner loop now, and the overall impact is more dramatic since we're doing everything twice. |
Sorry, something went wrong.
|
Rebased to main |
Sorry, something went wrong.
Fix test
|
This has fallen well off the PRs front page so is probably getting lost, @timhoffm any thoughts? |
Sorry, something went wrong.
|
I'm uneasy about introducing complex caches. Caching a pure function is fine, but here we have to jump substantial hoops with conditional use of caching and cache invalidation. Touching ~20 code locations for that seems brittle and not something I would want to understand or maintain. I suspect this need for distributed caching is the result of our bad/fragmented tick handling (and possibly it's even the root cause for the tick inefficienty). I still think something like #5665 (comment) is the prerequisite to get out of the tick mess. |
Sorry, something went wrong.
|
Agree that this is pretty complex state & fragile with respect to future refactors / maintainability. Offsetting that is that ticks are covered by pretty much every single image comparison test so I'm not too worried about regressions. Will leave open for now if anyone else wants to weigh in. I think the speedup here is significant enough to warrant it, but we did get a good boost on overall ticks performance from #30995 so I'm less gung-ho about pushing this one. The 3D tick handling is actually a good bit simpler than 2D, so I'll ping you on looking at caching for those when I finish up #30994 |
Sorry, something went wrong.
|
I'm afraid that the complex cache makes future refactoring like #5665 (comment), that could substantially improve the tick architecture and performance much harder. How much faster does the test suite get by this? |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
PR summary
Towards #5665
Currently we are calling _update_ticks() 3 times per axis every draw call, and _get_ticklabel_bboxes 2 times per axis. These calls take up about 35% of total draw time for an empty plot.
We can eliminate 25% of total draw time by caching the results of these calculations every draw cycle. This introduces some state, but I think it's decently well guarded and the performance boost is definitely worth it.
Before & After
The circled red areas show the before & after runtime of axis._update_label_position within Axis.draw(). It runs after the cache values have been set by other functions, so we nearly completely eliminate its runtime.
Before:

After:

Profiling script:
PR checklist