python - 为什么 "if x:"明显快于 "if x>0"?康威的人生游戏

标签 python python-3.x optimization

所以我正在用 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] > 0if 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)
额外的 > 1int 引入更少的开销比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/

相关文章:

python - KivyMD 小部件显示在 MDBoxLayout 中的页面底部

python - 从 sklearn.model_selection.GridSearchCV 获取 keyerror

python - Pandas - 在列中的 groupby 之后连接字符串,忽略 NaN,忽略重复项

python - 在 Python 3.5.1 文档中找不到方法 regex.scanner(),但解释器运行良好

python - 给定位数时生成 66666 等数字的最快方法

python - 相关查询中的 Django 元排序

python - 如何修复 "Can' t find a default Python”错误

c# - 通过网络优化 Dictionary.EnumerateFiles

ios - SpriteKit 游戏中的帧速率极慢

c - 在优化 C 中的分段素筛时卡住了