正如 Ioannis 和 DavidW 告诉你的,你不应该创建一个 Python 对象的 c 数组,而应该使用一个 Python 列表。
对生成的纯 Python 进行 Cytonizing 会带来大约 2 倍的加速,因为 cython 会删除解释器部分。但是,如果您还可以摆脱引用计数和动态调度,那么还有更多的潜力——高达 100 倍的加速是很常见的。前段时间我回复了a question,说明了这一点。
您应该怎么做才能加快速度?您需要将 python-multiplication 替换为“bare metal”乘法。
第一步:不要为particle 使用 (python)-class,使用简单的 c-struct - 它只是数据的集合 - 仅此而已:
cdef struct particle:
double x
double y
double pot
第一个好处:可以定义这些结构的全局 c 数组(另一个问题是,在更大的项目中这样做是否非常聪明):
DEF n=2000 # known at compile time
cdef particle parlist[n]
数组初始化后(更多细节见附件),我们可以在calcpot函数中使用它(我稍微改变了你的定义):
def calcpot():
cdef double pot,dX,dY
cdef int i,j
for i in range(n):
pot=0.0
for j in range(i+1, n):
dX=parlist[i].x-parlist[j].x
dY=parlist[i].y-parlist[j].y
pot=pot+1.0/(dX*dX+dY*dY)
parlist[i].pot=pot
与原代码的主要区别:parlist[i].xand Co. 不再是慢的 python 对象,而是简单快速的doubles。为了能够获得最大的加速,需要考虑很多微妙的事情 - 真的应该阅读/重读cython documentation。
这些麻烦值得吗?我的机器上有计时(通过%timeit calcpot()):
Time Speed-up
pure python + interpreter: 924 ms ± 14.1 ms x1.0
pure python + cython: 609 ms ± 6.83 ms x1.5
cython version: 4.1 ms ± 55.3 µs x231.0
通过使用低级结构加速231!
列出python代码:
import random
class particle:
def __init__(self):
self.x=random.uniform(1,99)
self.y=random.uniform(1,99)
self.pot=0
n=2000
parlist = [particle() for _ in range(n)]
def calcpot():
for i in range(n):
pot=0.0
for j in range(i+1, n):
dX=parlist[i].x-parlist[j].x
dY=parlist[i].y-parlist[j].y
pot=pot+1.0/(dX*dX+dY*dY)
parlist[i].pot=pot
列出cython代码:
#call init_parlist prior to calcpot!
cdef struct particle:
double x
double y
double pot
DEF n=2000 # known at compile time
cdef particle parlist[n]
import random
def init_parlist():
for i in range(n):
parlist[i].x=random.uniform(1,99)
parlist[i].y=random.uniform(1,99)
parlist[i].pot=0.0
def calcpot():
cdef double pot,dX,dY
cdef int i,j
for i in range(n):
pot=0.0
for j in range(i+1, n):
dX=parlist[i].x-parlist[j].x
dY=parlist[i].y-parlist[j].y
pot=pot+1.0/(dX*dX+dY*dY)
parlist[i].pot=pot