所以我正在用 Python 3 制作康威的生命游戏,我有一个名为 updateboard
的函数它根据存储在 self.neighbors
中的邻居计数(从 0 到 8)来生产和杀死细胞.该函数如下所示:
def updateboard(self):
"""
Updates the board state
"""
alive = np.zeros((self.cellsize, self.cellsize), dtype=np.bool) # board state for next loop
# iterate cells
for y in range(self.cellsize):
for x in range(self.cellsize):
if self.neighbors[x, y] > 0: # only look at cells with more than 1 neighbors
if self.board[x, y]: # cell is currently alive
alive[x, y] = True if self.neighbors[x, y] in (2, 3) else False
else: # cell is currently dead
alive[x, y] = True if self.neighbors[x, y] == 3 else False
# update states
self.updateneighbors(self.board, alive)
self.board = alive
为了避免多余的检查,我正在检查是否 self.neighbors
在决定细胞是生还是死之前,该细胞大于 0。我尝试了不同的方法来优化这个功能,我发现改变
if self.neighbors[x, y] > 0
至 if self.neighbors[x, y]
显着加快了功能。运行 python 分析器显示了这一更改如何使函数速度提高近 6 倍。
之前
ncalls tottime percall cumtime percall filename:lineno(function)
459 12.465 0.027 13.916 0.030 logic.py:55(updateboard)
后 ncalls tottime percall cumtime percall filename:lineno(function)
460 1.619 0.004 3.067 0.007 logic.py:55(updateboard)
我尝试在网上寻找解释并发现了许多类似的问题,但还没有设法找到这个特定问题的答案。我很困惑也很惊讶这个小小的改变如何产生如此大的不同,如果有人能帮助我解释这一点,我将不胜感激。
最佳答案
您通过一次迭代一个元素来处理 NumPy 数组,而不是将工作推送到优化的 NumPy C 级代码中。这根本没有利用 NumPy 的强大功能。这就像你走到商店时用手拖着你的车,而不是开车。
NumPy 不是为单元素操作而设计的。它支持它们,但假设单元素操作很少见。 NumPy 对单个元素的大多数操作都有巨大的开销,例如 >
.它需要进行大量的调度、类型转换和包装对象分配,而这在使用普通 Python 列表和标量时不会发生。
简单的实验表明,这种开销与普通 Python ints 发生的情况完全不同:
In [1]: x = 1
In [2]: %timeit if x: pass
26.1 ns ± 0.0316 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [3]: %timeit if x > 1: pass
35.6 ns ± 0.315 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [4]: import numpy
In [5]: x = numpy.int64(1)
In [6]: %timeit if x: pass
26.8 ns ± 0.0422 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [7]: %timeit if x > 1: pass
203 ns ± 7.63 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
额外的 > 1
为 int
引入更少的开销比numpy.int64
.请注意,
if x
中涉及的隐式 bool 解释对于 NumPy 来说,这是一个比 x > 1
简单得多的操作.没有第二个参数可以调度或转换,C 级 nb_bool
hook 不需要创建包装对象。 NumPy 执行此操作的速度几乎与普通 Python 标量一样快。为了真正加快你的代码,你应该在数组而不是元素方面工作:
alive = (self.neighbors == 3) | ((self.neighbors == 2) & self.board)
然后 NumPy 将能够直接在 C 级别循环遍历数组的缓冲区,从而大大降低开销。
关于python - 为什么 "if x:"明显快于 "if x>0"?康威的人生游戏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67747896/