我需要优化这个功能,因为我正试图使我的opengl模拟运行得更快。我想使用Parakeet,但我不太明白要修改下面的代码需要什么方式你知道我该怎么做吗?

def distanceMatrix(self,x,y,z):
    " ""Computes distances between all particles and places the result in a matrix such that the ij th matrix entry corresponds to the distance between particle i and j"" "
    xtemp = tile(x,(self.N,1))
    dx = xtemp - xtemp.T
    ytemp = tile(y,(self.N,1))
    dy = ytemp - ytemp.T
    ztemp = tile(z,(self.N,1))
    dz = ztemp - ztemp.T

    # Particles 'feel' each other across the periodic boundaries
    if self.periodicX:
        dx[dx>self.L/2]=dx[dx > self.L/2]-self.L
        dx[dx<-self.L/2]=dx[dx < -self.L/2]+self.L
    if self.periodicY:
        dy[dy>self.L/2]=dy[dy>self.L/2]-self.L
        dy[dy<-self.L/2]=dy[dy<-self.L/2]+self.L
    if self.periodicZ:
        dz[dz>self.L/2]=dz[dz>self.L/2]-self.L
        dz[dz<-self.L/2]=dz[dz<-self.L/2]+self.L

    # Total Distances
    d = sqrt(dx**2+dy**2+dz**2)

    # Mark zero entries with negative 1 to avoid divergences
    d[d==0] = -1

    return d, dx, dy, dz

据我所知,长尾鹦鹉应该可以使用上面的函数而无需修改-它只使用numpy和math。但是,当从Parakeet jit包装器调用函数时,我总是会遇到以下错误:
AssertionError: Unsupported function: <bound method Particles.distanceMatrix of <particles.Particles instance at 0x04CD8E90>>

最佳答案

Parakeet还很年轻,它对Numpy的支持还不完整,而且您的代码涉及到了一些尚不起作用的特性。
1)你在包装一个方法,而到目前为止鹦鹉只知道如何处理函数。常见的解决方法是生成一个@jit包装的helper函数,并使用所有必需的成员数据对其进行方法调用。方法不起作用的原因是,为“self”分配一个有意义的类型是非常重要的。这不是不可能的,但足够狡猾的方法不会使他们的方式进入鹦鹉,直到较低的挂果被采摘。说到低垂的水果…
2)布尔索引。尚未实现,但将在下一个版本中。
3)np.tile:也不工作,也可能会在下一个版本中。如果您想了解哪些内置函数和NumPy库函数可以工作,请查看Parakeet的mappings模块。
我重写了你的代码以便对鹦鹉更友好一点:

@jit
def parakeet_dist(x, y, z, L, periodicX, periodicY, periodicZ):
  # perform all-pairs computations more explicitly
  # instead of tile + broadcasting
  def periodic_diff(x1, x2, periodic):
    diff = x1 - x2
    if periodic:
      if diff > (L / 2): diff -= L
      if diff < (-L/2): diff += L
    return diff
  dx = np.array([[periodic_diff(x1, x2, periodicX) for x1 in x] for x2 in x])
  dy = np.array([[periodic_diff(y1, y2, periodicY) for y1 in y] for y2 in y])
  dz = np.array([[periodic_diff(z1, z2, periodicZ) for z1 in z] for z2 in z])
  d= np.sqrt(dx**2 + dy**2 + dz**2)

  # since we can't yet use boolean indexing for masking out zero distances
  # have to fall back on explicit loops instead
  for i in xrange(len(x)):
    for j in xrange(len(x)):
      if d[i,j] == 0: d[i,j] = -1
  return d, dx, dy, dz

在我的机器上,当n=2000时,它的运行速度只有numpy的3倍(numpy为0.39秒,而parakeet为0.14秒)。如果我重写数组遍历以更明确地使用循环,那么性能将比numpy快约6倍(parakeet在约0.06s内运行):
@jit
def loopy_dist(x, y, z, L, periodicX, periodicY, periodicZ):
  N = len(x)
  dx = np.zeros((N,N))
  dy = np.zeros( (N,N) )
  dz = np.zeros( (N,N) )
  d = np.zeros( (N,N) )

  def periodic_diff(x1, x2, periodic):
    diff = x1 - x2
    if periodic:
      if diff > (L / 2): diff -= L
      if diff < (-L/2): diff += L
    return diff

  for i in xrange(N):
    for j in xrange(N):
      dx[i,j] = periodic_diff(x[j], x[i], periodicX)
      dy[i,j] = periodic_diff(y[j], y[i], periodicY)
      dz[i,j] = periodic_diff(z[j], z[i], periodicZ)
      d[i,j] = dx[i,j] ** 2 + dy[i,j] ** 2 + dz[i,j] ** 2
      if d[i,j] == 0: d[i,j] = -1
      else: d[i,j] = np.sqrt(d[i,j])
  return d, dx, dy, dz

通过一点创造性的重写,你也可以让上面的代码在numba中运行,但它只比numpy快1.5倍(0.25秒)。编译时间为Parakeet w/理解:1秒,Parakeet w/循环:.5秒,Numba w/循环:0.9秒。
希望接下来的几个版本能够更惯用地使用numpy库函数,但现在理解或循环通常是要做的事情。

07-28 07:57