Skip to content

Speed up Tkagg blit with Tk_PhotoPutBlock #20840

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

Merged
merged 14 commits into from
Aug 20, 2021

Conversation

richardsheridan
Copy link
Contributor

@richardsheridan richardsheridan commented Aug 15, 2021

PR Summary

This PR releases the GIL while blitting, and chooses a slightly different but sometimes faster function to actually perform the blit.

In my usage, blitting can often be over 100 ms, which is a long time to hold the GIL... especially while not using the Python C API. I verified the safety of releasing the GIL back in 2ad422a but for some reason I took it back out when thread-based parallelism didn't work out. (#18565)

_tkagg.blit has been backed by Tk_PhotoPutBlock_NoComposite since #6442. This interface is deprecated, and on Tk 8.5 and later Tk_PhotoPutBlock gives the option to composite or not as a function argument, avoiding the need for an extra blanking step and permitting a fast path when the data are properly aligned, which seems to be the case on TkAgg. My profiling shows that the time spent specifically in _tkagg.blit drops by a factor of 2 to 10 (!?) in the naive use case of calling fig.canvas.draw(). However, FigureCanvasAgg.draw is the main time sink in either case, so the FPS gains are limited, and for large canvases blitting still takes enough time to make dropping the GIL worthwhile. Nevertheless, I get a practical responsivity boost in a downstream application (100 ms -> 10 ms blit latency) so I think it is worth sharing.

If anyone can tell me why this speedup is only visible when Figure.get_frameon()==True, I will sleep much better at night. It would seem to have to do with the abundance or paucity of transparent pixels in the PhotoImage, but the Tk source doesn't inspect that directly. Could it boil down to CPU branch prediction?

This change is backwards-incompatible with Tk < 8.5. I don't think that affects Python 3 or Matplotlib 3 users, but I would like confirmation. Also I would like someone to check if this change is problematic in the TkCairo backend

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • [ ] New features are documented, with examples if plot related.
  • [x] Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • [ ] Conforms to Matplotlib style conventions (install flake8-docstrings and run flake8 --docstring-convention=all).
  • [ ] New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

Also release GIL

Currently, drawing an equivalence between blank and TK_PHOTO_COMPOSITE_SET which may not be entirely accurate
@anntzer
Copy link
Contributor

anntzer commented Aug 16, 2021

This change is backwards-incompatible with Tk < 8.5.

Based on tcltk/tk@8abf694 this seems backcompatible to as far back as tk 8.4.0 (2002)? Or did I miss something? It's not clear why this was changed in #6442.

I don't think that affects Python 3 or Matplotlib 3 users, but I would like confirmation.

8.4.0 seems certainly old enough that we can require it, although a doc note may be good (I can't find any mention in the cpython docs stating that it cannot be built against tk 8.3, although that may be a bit academic).

Also I would like someone to check if this change is problematic in the TkCairo backend

At least you'll need to simimarly adjust the call to _backend_tk.blit in backend_tkcairo.py.

@richardsheridan
Copy link
Contributor Author

Based on tcltk/tk@8abf694 this seems backcompatible to as far back as tk 8.4.0 (2002)? Or did I miss something? It's not clear why this was changed in #6442.

I based 8.5 on a fuzzy reading of the docs, if it's there in 8.4, then it's even less of an issue

8.4.0 seems certainly old enough that we can require it, although a doc note may be good (I can't find any mention in the cpython docs stating that it cannot be built against tk 8.3, although that may be a bit academic).

If you can point me to roughly the right file in the docs, I'm happy to make a change. Probably a .. warning:: or .. note::, right?

At least you'll need to simimarly adjust the call to _backend_tk.blit in backend_tkcairo.py.

The call signature of _backend_tk.blit hasn't changed, only _tkagg.blit. Maybe it is worth giving them different names? It would make debugging/profiling a little less confusing too.

@anntzer
Copy link
Contributor

anntzer commented Aug 16, 2021

If you can point me to roughly the right file in the docs, I'm happy to make a change. Probably a .. warning:: or .. note::, right?

There's a list in doc/devel/dependencies.rst; an api_changes entry seems safe too.

Maybe it is worth giving them different names?

Yes, that's a bit confusing. It's all private anyways, so they can be changed at will.


Looks like tkcairo works fine with this (well, possibly modulo some preexisting compositing issues, but they are not introduced by the changes here).

@richardsheridan richardsheridan added this to the v3.5.0 milestone Aug 16, 2021
@anntzer
Copy link
Contributor

anntzer commented Aug 16, 2021

If anyone can tell me why this speedup is only visible when Figure.get_frameon()==True, I will sleep much better at night. It would seem to have to do with the abundance or paucity of transparent pixels in the PhotoImage, but the Tk source doesn't inspect that directly. Could it boil down to CPU branch prediction?

I don't really know, but it does seem that successfully always predicting alpha == 255 at https://github.com/tcltk/tk/blob/5745147320011c82db86941f349d25a690cf914b/generic/tkImgPhoto.c#L3440 could indeed help.

@QuLogic
Copy link
Member

QuLogic commented Aug 19, 2021

Looking at repology, I don't see anything that ships <8.4, so it's probably quite fine to bump to it.

@richardsheridan
Copy link
Contributor Author

Suggest squash merge

@tacaswell tacaswell merged commit 7b9e1ce into matplotlib:master Aug 20, 2021
@richardsheridan richardsheridan deleted the tkagg_photoputblock branch September 2, 2021 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants