Python:调用函数真的很慢吗?
我有一个叫做 Element
的类,这个类里面有一些函数,其中一个是这样的:
def clean(self):
self.dirty = False
我有1024个元素,我在一个 while 1:
循环里对每一个元素都调用 clean
方法。
如果我停止调用 clean
方法,游戏的帧率从76帧每秒(fps)一下子提升到250帧每秒。
这让我感到很困扰。难道我真的需要这么小心,才能避免我的代码完全卡住吗?
编辑(这是完整的代码):
250 fps 的代码
for layer in self.layers:
elements = self.layers[layer]
for element in elements:
if element.getDirty():
element.update()
self.renderImage(element.getImage(), element.getRenderPos())
element.clean()
76 fps 的代码
for layer in self.layers:
elements = self.layers[layer]
for element in elements:
if element.getDirty():
element.update()
self.renderImage(element.getImage(), element.getRenderPos())
element.clean()
2编辑(这是性能分析的结果):
Sat Feb 9 22:39:58 2013 stats.dat
23060170 function calls (23049668 primitive calls) in 27.845 seconds
Ordered by: internal time
List reduced from 613 to 20 due to restriction <20>
ncalls tottime percall cumtime percall filename:lineno(function)
3720076 5.971 0.000 12.048 0.000 element.py:47(clean)
909 4.869 0.005 17.918 0.020 chipengine.py:30(updateElements)
3742947 4.094 0.000 5.443 0.000 copy.py:67(copy)
4101 3.972 0.001 3.972 0.001 engine.py:152(killScheduledElements)
11773 1.321 0.000 1.321 0.000 {method 'blit' of 'pygame.Surface' objects}
4 1.210 0.302 1.295 0.324 resourceloader.py:14(__init__)
3720076 0.918 0.000 0.918 0.000 element.py:55(getDirty)
1387 0.712 0.001 0.712 0.001 {built-in method flip}
3742947 0.705 0.000 0.705 0.000 copy.py:102(_copy_immutable)
3728284 0.683 0.000 0.683 0.000 {method 'copy' of 'pygame.Rect' objects}
3743140 0.645 0.000 0.645 0.000 {method 'get' of 'dict' objects}
5494 0.566 0.000 0.637 0.000 element.py:89(isBeclouded)
2296 0.291 0.000 0.291 0.000 {built-in method get}
1 0.267 0.267 0.267 0.267 {built-in method init}
1387 0.244 0.000 25.714 0.019 engine.py:67(updateElements)
2295 0.143 0.000 0.143 0.000 {method 'tick' of 'Clock' objects}
11764 0.095 0.000 0.169 0.000 element.py:30(update)
8214/17 0.062 0.000 4.455 0.262 engine.py:121(createElement)
40 0.052 0.001 0.052 0.001 {built-in method load_extended}
36656 0.046 0.000 0.067 0.000 element.py:117(isCollidingWith)
2 个回答
0
(这是个评论,但我的等级太低,不能“评论”)
如果你调用 element.getDirty() 这个方法 370 万次,而它只有 1.1 万次是“脏”的(也就是需要更新的),那么你应该保持一个“脏”的列表,而不是每次都去检查。
也就是说,不要每次都设置“脏”的标记,而是把“脏”的元素添加到一个“脏”元素的列表里。看起来你可能需要为每一层都准备一个“脏”列表。
1
性能分析显示,调用清理方法在整个分析过程中大约花费了28秒中的6秒。而在这段时间里,这个方法被调用了370万次。
这意味着你展示的循环应该是软件的主循环。这个主循环主要做以下几件事:
- 检查元素是否“脏”。
- 如果是“脏”的,就绘制它。
- 然后清理它。
因为大多数元素并不是“脏”的(在这370万次循环中,update()方法只被调用了1.1万次),所以最终结果是你的主循环现在只在做一件事:检查元素是否“脏”,然后调用.clean()方法。
通过只在元素“脏”的时候才调用清理方法,你实际上把主循环的工作量减半了。
我真的需要这么小心,才能避免代码完全卡住吗?
是的。如果你有一个非常紧凑的循环,大部分时间又什么都不做,那么你必须确保这个循环确实是紧凑的。
这听起来让人不安。
其实这只是计算机的基本原理。