GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Refactor of `axis.py`/ticking should have large performance gains · Issue #5665 · matplotlib/matplotlib

Refactor of axis.py/ticking should have large performance gains #5665

New issue
Copy link
New issue
Copy link
Open
Open
Refactor of axis.py/ticking should have large performance gains#5665
Copy link
Labels
Difficulty: Mediumhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issueshttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issuesPerformancekeepItems to be ignored by the “Stale” Github ActionItems to be ignored by the “Stale” Github Actiontopic: ticks axis labels
Milestone
future releases

Description

@mdboom
mdboom
opened on Dec 11, 2015
Issue body actions

Right now, each Axis has a two lists of Tick objects (one major, one minor), each of which has 3 Line objects (for left tick, right tick and gridline) and 2 Text objects (for main and secondary label). Constructing Line objects is rather heavy and expensive. Refactoring this code so that each Axis had 3 LineCollection objects instead where only the points of the ticks needs to be updated, sharing all of the line style information, should be much faster.

On the following simple benchmark:

plt.subplots(8, 8)
plt.savefig('test.png')

25% of the time is spent constructing the Line objects. The suggested change above should reduce the number of Line objects from 3*N (where N is the number of ticks, usually a dozen or so) to 3.

Activity

Next
  1. mdboom
    added
    Difficulty: Mediumhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issueshttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issues
    on Dec 11, 2015
  2. Tillsten commented on Dec 11, 2015

    @Tillsten
    Tillsten
    on Dec 11, 2015
    Contributor
    More actions

    I think i have a prototype doing that, if no one works on this before i will have a look after christmas. The whole axis code seems quite convoluted for what it does.

  3. anntzer
    added
    Performance
    on Mar 19, 2018
  4. timhoffm commented on Mar 28, 2018

    @timhoffm
    timhoffm
    on Mar 28, 2018
    Member
    More actions

    This looks like an interesting topic with some performance gain and not too difficult (once we've figured out the right structure). 😄
    I may embark on the topic. Some first thoughts:

    Current implementation:

    Axis
    - major: Ticker
    - minor: Ticker
    - majorticks: List[Tick]
    - minorticks: List[Tick]
    
    Ticker
    - locator: Locator
    - formatter: Formatter
    
    Tick
    - tick1line: Line
    - tick2line: Line
    - gridline: Line
    - label1: Text
    - label2: Text
    

    Possible alternative implementation (first try):

    Axis
    - majorticks: TickList
    - minorticks: TickList
    
    TickList
    - locator: Locator
    - formatter: Formatter
    - ticks: LineCollection
    - secondary_ticks: LineCollection
    - gridlines: LineCollection
    - ticklabels: List[Text]
    - secondary_ticklabels: List[Text]
    

    This feels significantly better and should be doable with reasonable backward compatibility (full compatibility may be difficult or at least require some extra work, if users really descend to the Artists of individual ticks). I'm not sure if and how much incompatibility we want to allow here.

    Structuring by major/minor and then by primary/secondary may not be the best option. One could swap the order so that

    Axis
    - ticks: AxisTicks
    - secondary_ticks: AxisTicks
    
    AxisTicks
    - major: LineCollection
    - minor: LineCollection
    ...
    

    This would potentially allow different ticks on the secondary axis. Also it could make it simpler to handle interactions between minor and major ticks (e.g. leave out minor ticks at the position of major ticks).

    Reacted by Elan Ernest
  5. tacaswell
    added this to the This milestone has been deleted milestone on Mar 28, 2018
  6. anntzer commented on Mar 28, 2018

    @anntzer
    anntzer
    on Mar 28, 2018
    Contributor
    More actions

    @timhoffm I had started giving a try to the idea (not pursuing it actively now), per https://github.com/anntzer/matplotlib/tree/_wip/newaxis (taking an approach of "delete everything and reimplement stuff on top of it"). Feel free to scavenge stuff from that.

    A few random notes:

    • LineCollections don't support markers, so if you use them for ticks each tick has to become a line itself, rather than being represented by a marker. Conversely, using a single line object to represent all ticks (what I've done right now) makes controlling properties of individual ticks close to impossible.
    • I think that it'll be very difficult to maintain backcompat on a project like that; however, a possibility (noted in this week's call) would be to have a new, separate Axes class that uses these new Axises (while also maintaining the user-facing plotting methods); one could then choose to use the new Axes class by (ab)using the projection system.
  7. jklymak commented on Mar 28, 2018

    @jklymak
    jklymak
    on Mar 28, 2018
    Member
    More actions

    This was discussed a bit during the call this week.

    In working on #10841, I found it pretty strange that the formatter only operated on one tick at a time, and the formatter ended up looping through all the ticks on the first tick and then caching the labels for all the ticks. So, a reorganization like this makes a lot of sense to me - I think there are more use cases where you want to treat the ticks as a list rather than being able to do something (?) to individual ticks.

  8. anntzer commented on Mar 28, 2018

    @anntzer
    anntzer
    on Mar 28, 2018
    Contributor
    More actions

    #5804 is possibly relevant re: formatter work too.

  9. efiring commented on Mar 28, 2018

    @efiring
    efiring
    on Mar 28, 2018
    Member
    More actions

    I suspect the need to manipulate individual ticks is extremely rare. It's possible that the tick handling pre-dated the introduction of collections.

  10. anntzer commented on Mar 28, 2018

    @anntzer
    anntzer
    on Mar 28, 2018
    Contributor
    More actions

    One case (not that I use it, but doesn't sounds too crazy) is https://matplotlib.org/examples/api/skewt.html, where you can see that for some of the slanted gridlines, the associated tick is not visible.

  11. timhoffm commented on Mar 28, 2018

    @timhoffm
    timhoffm
    on Mar 28, 2018
    Member
    More actions

    @anntzer Thanks for the example. It helps to have an idea what could all be needed. This needed a special implementation for XTick. The same solution for a future TickCollection (or whatever it's called) will probably need a special subclass of that.

    @ all: Generally, can we agree that in the default implementation all ticks of a kind (major/minor) will have the same properties? It's still always possible to tell the locator to leave out certain positions and draw something custom there yourself.

  12. anntzer commented on Mar 28, 2018

    @anntzer
    anntzer
    on Mar 28, 2018
    Contributor
    More actions

    I just realized that that specific example could in fact be made to work by clipping the x-ticks to a bounding box that bounds the horizontal direction but not the vertical one -- perhaps a better idea than manipulating tick visibility...).

  13. tacaswell commented on Mar 28, 2018

    @tacaswell
    tacaswell
    on Mar 28, 2018
    Member
    More actions

    I suspect we will need to aggressively shim the back compatibility on anything that is public API (and maybe even some private stuff). Users are extremely clever and find all the corner cases.

    separating the primary/secondary ticks is probably a good idea as that makes in easy(ier) to put different locators and tickers on each of them.

    It is probably worth going through the axis_grid implementation of ticks and axis objects carefully to understand what it does differently, what of it we want to pull up to mainline, and what lessons we should learn.

  14. anntzer commented on Mar 28, 2018

    @anntzer
    anntzer
    on Mar 28, 2018
    Contributor
    More actions

    ... actually playing with the clipbox won't work in the example above as labels will not be cropped out properly (they may only be half-clipped).

    @tacaswell If we move stuff to a separate "projection" (as proposed above) the new implementation would be strictly opt-in. It doesn't mean we should gratuitiously break things but given that the current proposals don't even really include the notion of "individual" ticks (everything goes into a Collection object), any shimming is going to be tricky too...

  15. tacaswell commented on Mar 28, 2018

    @tacaswell
    tacaswell
    on Mar 28, 2018
    Member
    More actions

    I responded not having read the rest of the discussion (because I did not reload my browser), sorry.

    Putting in it a separate projection is a good way to make progress and side-step back compatibility issues, but I worry that we will end up with a third full independent implementation of ticks and friends in the code base.

  16. 22 remaining items

  17. QuLogic
    modified the milestones: v3.5.0, v3.6.0 on Sep 25, 2021
  18. QuLogic
    modified the milestones: v3.6.0, v3.7.0 on Jul 8, 2022
  19. QuLogic
    modified the milestones: v3.7.0, future releases on Jan 26, 2023
  20. github-actions commented on Jan 29, 2024

    @github-actions
    github-actionsBot
    on Jan 29, 2024 – with GitHub Actions
    More actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  21. github-actions
    added
    status: inactiveMarked by the “Stale” Github ActionMarked by the “Stale” Github Action
    on Jan 29, 2024
  22. timhoffm commented on Jan 29, 2024

    @timhoffm
    timhoffm
    on Jan 29, 2024
    Member
    More actions

    #5665 (comment) Still holds

  23. timhoffm
    added
    keepItems to be ignored by the “Stale” Github ActionItems to be ignored by the “Stale” Github Action
    and removed
    status: inactiveMarked by the “Stale” Github ActionMarked by the “Stale” Github Action
    on Jan 29, 2024
  24. scottshambaugh commented on Jan 8, 2025

    @scottshambaugh
    scottshambaugh
    on Jan 8, 2025
    Contributor
    More actions

    Another datapoint from #29435 that even with plotted data, ticks operations are taking up ~half the draw time on basic 3D plots (profiled using py-spy):

    image

  25. tacaswell commented on Jan 9, 2025

    @tacaswell
    tacaswell
    on Jan 9, 2025
    Member
    More actions

    This is project is included in https://github.com/matplotlib/2024-NASA-ROSES-F7

    Reacted by Tim Hoffmann
  26. timhoffm commented on Sep 21, 2025

    @timhoffm
    timhoffm
    on Sep 21, 2025
    Member
    More actions

    I just did some profiling today, because I was annoyed by the duration of the test suite. For

    fig, ax = plt.subplots()
    ax.plot([1, 3, 2])
    

    Basically all time goes into Axes.__init__, more specifically Axes.__clear. We more or less know that already from earlier, but to state this explicitly: Axes initialization is the limiting factor.

    Within Axes.__clear(), 30% goes into this line

    matplotlib/lib/matplotlib/axes/_base.py

    Line 1355 in f8400db

    self.grid(False) # Disable grid on init to use rcParameter

    which in the end calls Axis.set_tick_params; and I suspect that this triggers tick creation.

  27. QuLogic commented on Sep 22, 2025

    @QuLogic
    QuLogic
    on Sep 22, 2025
    Member
    More actions

    These loops are likely triggers:

    matplotlib/lib/matplotlib/axis.py

    Lines 977 to 984 in f8400db

    if which in ['major', 'both']:
    self._major_tick_kw.update(kwtrans)
    for tick in self.majorTicks:
    tick._apply_params(**kwtrans)
    if which in ['minor', 'both']:
    self._minor_tick_kw.update(kwtrans)
    for tick in self.minorTicks:
    tick._apply_params(**kwtrans)

    but also probably this one:

    matplotlib/lib/matplotlib/axis.py

    Lines 1122 to 1123 in f8400db

    for child in self.majorTicks + self.minorTicks:
    child.set_clip_path(path, transform)

    I'm uncertain how to check if the descriptor is realized before accessing it. Actually, it looks like #16067 might lazify these two cases?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Difficulty: Mediumhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issueshttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issuesPerformancekeepItems to be ignored by the “Stale” Github ActionItems to be ignored by the “Stale” Github Actiontopic: ticks axis labels

    Type

    No type

    Projects

    No projects

      Milestone

      • future releasesNo due date

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402520 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.