利用Cython加速Python循环:从元胞自动机看性能提升
本文通过元胞自动机案例,详解如何使用Cython将Python双重循环转换为C扩展以提升执行效率。内容涵盖编写.pyx文件、配置setup.py编译脚本、处理NumPy类型定义及边界检查关闭,并展示实测结果:Cython版本较原生Python运行速度提升600倍以上,为计算密集型任务提供高效解决方案。
引言
在Python编程中,尤其是涉及多重循环的场景下,代码执行效率往往较低。参考文章https://www.jb51.net/article/133807.htm中关于元胞自动机的实现,其状态更新函数update_state采用了双重循环结构。为了改善这一性能瓶颈,本文尝试使用Cython对该方法进行了重构与优化。
代码实现
我们在项目同级目录下创建了一个名为update.pyx的文件,并编写了以下核心代码:
import numpy as np
cimport numpy as np
cimport cython
DTYPE = np.float
ctypedef np.float_t DTYPE_t
def update_state(np.ndarray[DTYPE_t, ndim=2] cells):
return update_state_c(cells)
@cython.boundscheck(False)
@cython.wraparound(False)
cdef np.ndarray[DTYPE_t, ndim=2] update_state_c(np.ndarray[DTYPE_t, ndim=2] cells):
"""更新一次状态"""
cdef unsigned int i
cdef unsigned int j
cdef np.ndarray[DTYPE_t, ndim=2] buf = np.zeros((cells.shape[0], cells.shape[1]), dtype=DTYPE)
cdef DTYPE_t neighbor_num
for i in range(1, cells.shape[0] - 1):
for j in range(1, cells.shape[0] - 1):
# 计算该细胞周围的存活细胞数
neighbor_num = cells[i, j-1] + cells[i, j+1] + cells[i+1, j] + cells[i-1, j] +\
cells[i-1, j-1] + cells[i-1, j+1] +\
cells[i+1, j-1] + cells[i+1, j+1]
if neighbor_num == 3:
buf[i, j] = 1
elif neighbor_num == 2:
buf[i, j] = cells[i, j]
else:
buf[i, j] = 0
return buf
代码中update_state_c函数上方的两个装饰器@cython.boundscheck(False)和@cython.wraparound(False)用于禁用Cython的边界检查机制,从而进一步提升循环执行的效率。
接着,我们在同一目录下创建setup.py文件,用于配置编译过程:
import numpy as np
from distutils.core import setup
from Cython.Build import cythonize
setup(
name="Cython Update State",
ext_modules=cythonize("update.pyx"),
include_dirs=[np.get_include()]
)
由于Cython代码中引用了NumPy的头文件,因此必须在setup.py中通过include_dirs参数指定NumPy的包含路径,以确保编译顺利进行。
执行命令python setup.py build_ext --inplace后,同级目录下会生成一个名为update.cp36-win_amd64.pyd的文件,这便是编译完成的C扩展模块。
接下来修改原始Python代码。首先在文件头部添加import update as cupdate导入编译后的模块,随后修改状态更新方法的调用逻辑如下:
def update_state(self): """更新一次状态""" self.cells = cupdate.update_state(self.cells) self.timer += 1
将原有的Python版本方法重命名为update_state_py,保持其他逻辑不变。运行脚本验证无异常后,即可进行性能对比测试。
性能测试
为了量化Cython带来的性能提升,我们编写了一个专门的测试函数来对比两者的执行时间。
def test_time():
import time
game = GameOfLife(cells_shape=(60, 60))
t1 = time.time()
for _ in range(300):
game.update_state()
t2 = time.time()
print("Cython Use Time:", t2 - t1)
del game
game = GameOfLife(cells_shape=(60, 60))
t1 = time.time()
for _ in range(300):
game.update_state_py()
t2 = time.time()
print("Native Python Use Time:", t2 - t1)
在配置好的测试环境中运行该测试方法,在我的电脑上得到的输出结果如下:
Cython Use Time: 0.007000446319580078 Native Python Use Time: 4.342248439788818
对比数据显示,Cython版本的执行速度提升了600多倍。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









