在尝试追踪matplotlib的plt.draw()函数的不可接受的(257ms
)运行时的来源时,我偶然发现了这篇文章:http://bastibe.de/2013-05-30-speeding-up-matplotlib.html。特别是,这句引起了我的注意:
“我在这里使用pause()来更新绘图而不会阻塞。正确的方法是使用draw()代替......”
进一步深入研究,我发现plt.draw()
可以用两个命令代替,
plt.pause(0.001)
fig.canvas.blit(ax1.bbox)
我的代码中分别占用256ms
和1ms
。
这是不正常的,为什么1ms
暂停需要256ms
完成?我拿了一些数据,发现了以下内容:
plt.pause(n):
n(s) time(ms) overhead(n(ms)-time(ms))
0.0001 270-246 ~246ms
0.001 270-254 ~253ms
0.01 280-265 ~255ms
0.1 398-354 ~254ms
0.2 470-451 ~251ms
0.5 779-759 ~259ms
1.0 1284-1250 ~250ms
numbers courtesy of rkern's line_profiler
这清楚地表明plt.pause()
做的不仅仅是暂停程序,而且我是正确的:
Line # Hits Time Per Hit % Time Line Contents
==============================================================
175 def pause(interval):
176 """
177 Pause for *interval* seconds.
178
179 If there is an active figure it will be updated and displayed,
180 and the GUI event loop will run during the pause.
181
182 If there is no active figure, or if a non-interactive backend
183 is in use, this executes time.sleep(interval).
184
185 This can be used for crude animation. For more complex
186 animation, see :mod:`matplotlib.animation`.
187
188 This function is experimental; its behavior may be changed
189 or extended in a future release.
190
191 """
192 1 6 6.0 0.0 backend = rcParams['backend']
193 1 1 1.0 0.0 if backend in _interactive_bk:
194 1 5 5.0 0.0 figManager = _pylab_helpers.Gcf.get_active()
195 1 0 0.0 0.0 if figManager is not None:
196 1 2 2.0 0.0 canvas = figManager.canvas
197 1 257223 257223.0 20.4 canvas.draw()
198 1 145 145.0 0.0 show(block=False)
199 1 1000459 1000459.0 79.5 canvas.start_event_loop(interval)
200 1 2 2.0 0.0 return
201
202 # No on-screen figure is active, so sleep() is all we need.
203 import time
204 time.sleep(interval)
once again courtesy of rkern's line_profiler
这是一个突破,因为突然清楚为什么plt.pause()
能够替换plt.draw()
,它内部有一个绘制函数,同时具有相同的〜250ms
开销我的计划的开始。
此时,我决定简要介绍plt.draw()
:
Line # Hits Time Per Hit % Time Line Contents
==============================================================
551 def draw():
571 1 267174 267174.0 100.0 get_current_fig_manager().canvas.draw()
好吧,又向兔子洞走了一步:
Line # Hits Time Per Hit % Time Line Contents
==============================================================
57 def draw_wrapper(artist, renderer, *args, **kwargs):
58 769 1798 2.3 0.7 before(artist, renderer)
59 769 242060 314.8 98.5 draw(artist, renderer, *args, **kwargs)
60 769 1886 2.5 0.8 after(artist, renderer)
不幸的是,这就是我通过源代码运行探查器的能力结束的地方,让我在draw()
函数的下一级别上摸不着头脑,以及为什么它被调用了769次。 / p>
事实证明,答案就在我面前!同样的文章,首先开始了整个强迫性狩猎,实际上是为了研究同样奇怪的行为而创建的!他们的解决方案:将plt.draw()
替换为需要更新的每个artist
,而不是每一个!
我希望我追逐这种行为可以帮助其他人理解它,虽然目前我每次尝试复制他的方法时都会遇到CGContextRef is NULL
错误,这似乎是MacOSX
特有的后端...
更多信息来了!请在下面的答案中添加更多相关信息,或者如果您可以帮我解决CGContextRef is NULL
错误。