Skip to content
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

pyplot.subplots() is slow #7163

Closed
timhoffm opened this issue Sep 22, 2016 · 9 comments
Closed

pyplot.subplots() is slow #7163

timhoffm opened this issue Sep 22, 2016 · 9 comments

Comments

@timhoffm
Copy link
Member

timhoffm commented Sep 22, 2016

matplotlib 1.5.3, Python 2.7.3, Linux (Ubuntu 14.04 LTS), installed using pip

pyplot.subplots() is significantly slower than pyplot.subplot(). This becomes significant when plotting a large number of subplots. For example subplots(40) takes 2.3s whereas 40 calls to subplot() with differing parameters just take 25ms.

Minimal example (using ipython):

import matplotlib.pyplot as plt
%timeit plt.subplots(40)
  • The effect does not depend on the grid: 40x1 is the same as 5x8.
  • The time needed increases linearly with the number of subplots.
  • The effect does not seem to depend on the parameters sharex, sharey and squeeze (though I have not tested all possible parameters)
  • profiling the call reveals that there are a lot of calls to reset_ticks, which use up 75% of the time. See the runsnake screenshot below:

matplotlib_subplots40

It seems there is a lot of unneccesary(?) clearing happening. Would it be possible to speed this up?

@Kojoley
Copy link
Member

Kojoley commented Sep 22, 2016

I think this is a duplicate of #6664

@QuLogic
Copy link
Member

QuLogic commented Sep 22, 2016

Probably related, but your issue doesn't seem to be specific to plt.subplots?

@timhoffm
Copy link
Member Author

timhoffm commented Sep 22, 2016

Yes, it's most likely the same cause. I didn't find it because subplots() is not mentioned.

Without knowing anything on the matplotlib internals, I'm still wondering why subplot() can be fast. After all, it has to create an Axes instance as well. Maybe that could be a hint for a solution?

If this issue doesn't add additional information, it may be closed as duplicate.

@Kojoley
Copy link
Member

Kojoley commented Sep 22, 2016

Probably related, but your issue doesn't seem to be specific to plt.subplots?

Yes, but

40 calls to subplot() with differing parameters just take 25ms.

creates exactly one Axes instance, while

subplots(40) takes 2.3s

creates 40 Axes.

@QuLogic
Copy link
Member

QuLogic commented Sep 22, 2016

40 calls to subplot() with differing parameters just take 25ms.

creates exactly one Axes instance, while

No, with suitably different parameters, you get 40 Axes just the same. There is only one Figure.

@Kojoley
Copy link
Member

Kojoley commented Sep 22, 2016

import matplotlib.pylab as plt
ax1 = plt.subplot()
ax2 = plt.subplot()
print('same' if ax1 is ax2 else 'different')

@QuLogic
Copy link
Member

QuLogic commented Sep 22, 2016

Of course that's the same; you're asking for the same subplot.

import matplotlib.pylab as plt
ax1 = plt.subplot(40, 1, 1)
ax2 = plt.subplot(40, 1, 2)
print('same' if ax1 is ax2 else 'different')

@Kojoley
Copy link
Member

Kojoley commented Sep 22, 2016

Yeah, that is strange:

>python -m timeit -s "import matplotlib.pylab as plt" "for i in range(9*9): plt.subplot(9, 9, i+1)"
10 loops, best of 3: 72.8 msec per loop

>python -m timeit -s "import matplotlib.pylab as plt" "plt.subplots(9*9)"
10 loops, best of 3: 2.12 sec per loop

@QuLogic
Copy link
Member

QuLogic commented Sep 22, 2016

Wait, I think I know what it is. I think timeit and some subtle difference between subplots and subplot are conspiring against us a bit, and it's related to the return of the same subplot as noted above.

What is missing is that the figure is never closed. plt.subplots always returns a new figure, but plt.subplot accesses the current figure and looks up the subplot with the same parameters. Thus all runs except the first appear really fast to timeit. If you ensure the figure is closed everything comes out similar:

In [3]: import matplotlib.pylab as plt

In [4]: def test():
   ....:     for i in range(40):
   ....:         plt.subplot(40, 1, i + 1)
   ....:         

In [5]: %timeit plt.subplots(40)
1 loops, best of 3: 1.04 s per loop

In [6]: %%timeit plt.close('all')
   ...: plt.subplots(40)
   ...: 
1 loops, best of 3: 1.03 s per loop

In [7]: plt.close('all')

In [8]: %timeit test()
The slowest run took 91.79 times longer than the fastest. This could mean that an intermediate result is being cached 
1 loops, best of 3: 11.2 ms per loop

In [9]: %%timeit plt.close('all')
   ...: test()
   ...: 
1 loops, best of 3: 1.01 s per loop

That warning there is the key: 91.79 * 11.2ms ==1.028048s for the first run.

So I'm going to close as a duplicate of #6664 then.

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

No branches or pull requests

3 participants