【问题标题】:c array with python class inside python function在python函数中带有python类的c数组
【发布时间】:2017-10-25 15:44:19
【问题描述】:

我在 Cython 工作。如何声明一个 python 类实例的 C 数组,然后将数组传递给 python 函数并对其进行处理?

cdef int n=100
class particle:
    def __init__(self):
        self.x=uniform(1,99)
        self.y=uniform(1,99)
        self.pot=0
cdef particle parlist[n]
def CalPot(parlist[]):
    for i in range(N-1):
        pot=0
        for j in range(i,N):
            dx=parlist[j].x-parlist[i].x
            dy=parlist[j].y-parlist[j].y
            r2=dx**2+dy**2
            pot=pot+4*ep*r2*((sig/r2)**12 - (sig/r2)**6)
        parlist[i].pot=pot

【问题讨论】:

标签: python arrays cython


【解决方案1】:

正如 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

【讨论】:

    【解决方案2】:

    BUG:使用空格,而不是制表符,REF:在 Markdown 中使用围栏代码块

    Python 类的实例是 Python 对象,在 Python 中处理得更好(它们不是 C 类型,我看不出有任何理由在 Cython 源代码中为它们创建某种形式的 C 表示)。此外,最好避免使用像 nparlist 这样的全局变量(在本例中它们不是必需的)。

    class particle:
        def __init__(self):
            self.x = uniform(1, 99)
            self.y = uniform(1, 99)
            self.pot = 0
    
    
    def CalPot(parlist):
        N = len(parlist)
        for i in range(N):
            pot = 0
            for j in range(i, N):
                dx = parlist[j].x - parlist[i].x
                dy = parlist[j].y - parlist[j].y
                r2 = dx**2 + dy**2
                pot = pot + 4 * ep * r2 * ((sig / r2)**12 - (sig / r2)**6)
            parlist[i].pot = pot
    

    所以这个 Cython 代码恰好是纯 Python。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-22
      • 1970-01-01
      • 2021-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      相关资源
      最近更新 更多