加速NumPy循环

时间:2010-10-22 20:23:52

标签: python performance numpy

我在Python中运行模型,我正在尝试加快执行时间。通过对代码进行分析,我发现在下面的cell_in_shadow函数中花费了大量的总处理时间。我想知道是否有办法加快速度?

该函数的目的是提供一个布尔响应,说明NumPy数组中的指定单元是否被另一个单元格遮蔽(仅在x方向上)。它通过向后沿行检查每个单元格的高度来实现这一点,它必须使给定的单元格处于阴影中。 shadow_map中的值由此处未显示的另一个函数计算 - 对于此示例,将shadow_map作为值类似于以下值的数组:

[0] = 0 (not used)
[1] = 3
[2] = 7
[3] = 18

add_x函数用于确保数组索引循环(使用时钟面算术),因为网格具有周期性边界(一侧的任何东西将重新出现在另一侧)。

def cell_in_shadow(x, y):
   """Returns True if the specified cell is in shadow, False if not."""

   # Get the global variables we need
   global grid
   global shadow_map
   global x_len

   # Record the original length and move to the left
   orig_x = x
   x = add_x(x, -1)

   while x != orig_x:
    # Gets the height that's needed from the shadow_map (the array index is the distance using clock-face arithmetic)
      height_needed = shadow_map[( (x - orig_x) % x_len)]
      if grid[y, x] - grid[y, orig_x] >= height_needed:
          return True

    # Go to the cell to the left
    x = add_x(x, -1)

def add_x(a, b):
   """Adds the two numbers using clockface arithmetic with the x_len"""
   global x_len

   return (a + b) % x_len

3 个答案:

答案 0 :(得分:3)

我同意Sancho认为Cython可能会成为可能的方式,但这里有几个小的加速:

一个。在启动while循环之前将grid[y, orig_x]存储在某个变量中,然后使用该变量。这将节省一大堆对网格数组的查询。

B中。因为你基本上只是从shadow_map中的x_len - 1开始并且一直工作到1,所以你可以避免使用模数这么多。基本上,改变:

while x != orig_x:
    height_needed = shadow_map[( (x - orig_x) % x_len)]

for i in xrange(x_len-1,0,-1):
    height_needed = shadow_map[i]

或者只是删除height_needed变量:

    if grid[y, x] - grid[y, orig_x] >= shadow_map[i]:

这些是微小的变化,但它们可能会有所帮助。

另外,如果你打算去Cython路线,我会考虑让你的功能为整个网格执行此过程,或者至少一次执行一行。这将节省大量的函数调用开销。但是,您可能无法真正执行此操作,具体取决于您使用结果的方式。

最后,您是否尝试过使用Psyco?它的工作量比Cython少,但它可能不会给你带来如此大的速度提升。我当然会先尝试一下。

答案 1 :(得分:2)

如果您不限于严格的Python,我建议您使用Cython。它可以允许索引的静态类型以及以c速度直接访问numpy数组的底层数据缓冲区。

查看http://wiki.cython.org/tutorials/numpy

上的简短教程/示例

在该示例中,执行的操作与您正在执行的操作非常相似(递增索引,访问numpy数组的各个元素),向索引变量添加类型信息会将时间减少一半。通过为numpy数组提供类型信息,将有效索引添加到numpy数组中,将时间缩短到原始数据的1%左右。

大多数Python代码已经是有效的Cython,所以你可以只使用你拥有的东西,并在需要的地方添加注释和输入信息,以便为你提供一些加速。

我怀疑您可以充分利用索引xyorig_x和numpy数组添加类型信息。

答案 2 :(得分:1)

以下指南比较了几种在python中优化数字代码的不同方法:

Scipy PerformancePython

有点过时,但仍然有用。请注意,它引用了pyrex,它已被分叉以创建Cython项目,如Sancho所述。

我个人更喜欢f2py,因为我认为fortran 90具有许多numpy的优点(例如,将两个数组与一个操作一起添加),但具有编译代码的全速。另一方面,如果你不了解fortran,那么这可能就不是了。

我简单地尝试了cython,我发现的麻烦是默认情况下cython生成的代码可以处理任意python类型,但仍然很慢。然后你必须花时间添加所有必要的cython声明,以使它更具体和快速,而如果你使用C或fortran,那么你将倾向于直接获得快速代码。同样,我已经熟悉这些语言,这是有偏见的,而如果Python是你所知道的唯一语言,Cython可能更合适。