-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Unnecessary drawing with NbAgg #13971
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
Comments
I just did some testing: import numpy as np
import matplotlib.pyplot as plt
i = 1000000
y = np.random.random(i)
x = np.random.random(i)
plt.figure()
plt.scatter(x,y)
@SylvainCorlay I see you're doing a lot of updates to ipympl these days (super job, btw). Have you seen anything that might affect this? |
Given #14596
is it still worth looking into performance issues of nbagg? |
@astrofrog it would be interesting if you could try out the master version of ipympl and let us know if this improves your experience. We are funding @martinRenou to put some cycles into |
Yes, I don't think the current logic in ipympl / nbagg is right. At the moment, @martinRenou is mostly doing a cleanup work / removing dead code / removing dependency to jquery. Goals of the pass by martin are:
Hopefully, a lot of the current glitches should be solved with this pass, and then the code base should be easier to move forward. Another goal is to have the ipympl front-end to be a bit more stateful, which would help for some of the embedding contexts.
Yes, I think so too. |
Concerning this "redraw on mouse move" issue, from what I understand I feel like matplotlib itself is requesting a redraw, so either we are sure that nothing changed and we don't redraw or we always redraw on request... The thing is that sometimes it really needs a redraw on mouse move (e.g. displaying a label on mouse hover). |
how are we shipping the cursor location text to the front end? I wonder if that is accidentally causing the re-draw? |
If I understand correctly, the cursor location text is sent from here: matplotlib/lib/matplotlib/backend_tools.py Line 337 in e39b334
And this code is triggered when the front-end sends a motion_notify event.
Actually, I tried with |
I don't see any indication that there's any drawing happening in your screencast, other than the "busy" indicator. I ran latest There are also some messages flipping the execution status from idle to busy and back. I think that Jupyter is just flipping execution status any time it has to run Python code that's not itself. Maybe with the binary protocol, this can use a different channel, or be flagged to not do that. |
Sorry, for some reason, I totally spaced out on the clicking part of the reproducer, which I can confirm. And I can reproduce that on latest master, though not yet sure what is triggering it. |
So the draw is only an empty blit because nothing's changed, but since transferring as base64 is not too efficient, it's a bit slow, though not as much as a full draw. Probably ipympl using binary transfer means this is less perceptible, but it may still be getting the extra draw. After a while of digging, the cause of the draw seems to come from the IPython post execute hook: matplotlib/lib/matplotlib/pyplot.py Lines 136 to 139 in da8789b
I don't think that hook is necessary in the notebook, so you could deregister it by calling plt.uninstall_repl_displayhook .
While that may be a suitable workaround for you, it's not the real cause because matplotlib/lib/matplotlib/_pylab_helpers.py Lines 133 to 136 in da8789b
|
Annnnd the reason it's stale is in a round about way related to #6664: File "matplotlib/lib/matplotlib/backend_bases.py", line 1754, in button_press_event
self.callbacks.process(s, mouseevent)
File "matplotlib/lib/matplotlib/cbook/__init__.py", line 225, in process
func(*args, **kwargs)
File "matplotlib/lib/matplotlib/backend_bases.py", line 1647, in pick
self.figure.pick(mouseevent)
File "matplotlib/lib/matplotlib/artist.py", line 513, in pick
a.pick(mouseevent)
File "matplotlib/lib/matplotlib/artist.py", line 513, in pick
a.pick(mouseevent)
File "matplotlib/lib/matplotlib/artist.py", line 502, in pick
for a in self.get_children():
File "matplotlib/lib/matplotlib/axis.py", line 751, in get_children
*self.get_major_ticks(), *self.get_minor_ticks()]
File "matplotlib/lib/matplotlib/axis.py", line 1325, in get_major_ticks
numticks = len(self.get_minorticklocs())
File "matplotlib/lib/matplotlib/axis.py", line 1243, in get_majorticklocs
major_locs = self.major.locator()
File "matplotlib/lib/matplotlib/ticker.py", line 2123, in __call__
return self.tick_values(vmin, vmax)
File "matplotlib/lib/matplotlib/ticker.py", line 2131, in tick_values
locs = self._raw_ticks(vmin, vmax)
File "matplotlib/lib/matplotlib/ticker.py", line 2070, in _raw_ticks
nbins = np.clip(self.axis.get_tick_space(),
File "matplotlib/lib/matplotlib/axis.py", line 2092, in get_tick_space
tick = self._get_tick(True)
File "matplotlib/lib/matplotlib/axis.py", line 1860, in _get_tick
return XTick(self.axes, 0, major=major, **tick_kw)
File "matplotlib/lib/matplotlib/axis.py", line 401, in __init__
super().__init__(*args, **kwargs)
File "matplotlib/lib/matplotlib/cbook/deprecation.py", line 387, in wrapper
return func(*inner_args, **inner_kwargs)
File "matplotlib/lib/matplotlib/axis.py", line 146, in __init__
self.apply_tickdir(tickdir)
File "matplotlib/lib/matplotlib/axis.py", line 449, in apply_tickdir
self.stale = True
File "matplotlib/lib/matplotlib/artist.py", line 226, in stale
self.stale_callback(self, val)
File "matplotlib/lib/matplotlib/artist.py", line 54, in _stale_axes_callback
self.axes.stale = val
File "matplotlib/lib/matplotlib/artist.py", line 226, in stale
self.stale_callback(self, val)
File "matplotlib/lib/matplotlib/figure.py", line 43, in _stale_figure_callback
self.figure.stale = val
File "matplotlib/lib/matplotlib/figure.py", line 246, in stale Button press starts picking -> |
Do we need a more general way to suppress the stale processing or is the problem that we are getting a new xtick? |
I guess #11027 is related. |
I don't think #11027 would have any effect on this. Current options seem to be:
Both of these fix the extra stale-ness, so if there's any preference, I can open a PR from either branch. I'm somewhat partial to the first method, but the possible effect on One approach I haven't tried is to check |
I'm also partial to the first as it is simpler at run time. |
First option would also "fail" if the user manually changed the fontsize of the tick and expects that to be taken into account in ticknumber selection. Carefully checking Axis.majorTicks for an already existing tick (without triggering unlazification!), i.e. the last suggestion, may work, though. The link to #11027 was more "if we're going to change things here, maybe we want to design something that'll sit better with #11027." |
If they changed the fontsize of a single tick, then that wouldn't have been taken into account in the current code either? But anyway, the And yes, the unlazification is the tricky part. |
I've put together the third option here: QuLogic@d0b2334 Opinions on which one? I think I still prefer the first one. |
I think I like the first one best. The second and third feel like they are spending too much effort to work around our selves which is sign we should reconsider what we are doing. It is a bit annoying that the |
Took me a little bit to figure out the test, as this is only triggered with mpl20 style, not classic, but I've opened #17348 with the first option. |
I am running into performance issues when using the NbAgg backend, some of which I think is avoidable. Essentially, when clicking on an NbAgg plot, with no tool active, the backend re-draws the data, which can cause the kernel to hang if the plot is complex. Here is a demonstration - note the kernel activity in the circle in the top right. Every time I click on the plot, it is redrawn:
Just to be clear, I totally expect plotting to be slow the first time the plot is shown, and if e.g. panning/zooming, but drawing when no tool is active doesn't seem right. This is causing issues for me because I am developing tools that respond to a click event in the plot and try and be nice by doing
draw_idle
to keep the UI responsive, but every mouse action also results in a fulldraw
which slows things down a lot.Matplotlib version
print(matplotlib.get_backend())
): NbAggThe text was updated successfully, but these errors were encountered: