【问题标题】:Speed up NumPy loop加速 NumPy 循环
【发布时间】:2010-10-22 20:23:51
【问题描述】:

我正在 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

【问题讨论】:

  • cell_in_shadow 中是否有比其他行更重的特定行?您是否尽可能不频繁地致电cell_in_shadow
  • 我不确定如何分辨哪些线条比其他线条重。你知道怎么做吗?我已经检查了我对它的所有调用,我只在需要时调用它。
  • @robintw:您是否为xy 的每个可能值调用cell_in_shadow
  • 是的,但是grid 数组在每次调用之间都会发生变化,所以我不能使用映射函数:(
  • @robintw 好吧,问题的很大一部分。如果有 M 行(x 坐标)和 N 列(y 坐标),那么您的算法本质上是 O(M^2*N)。

标签: python performance numpy


【解决方案1】:

我确实同意 Sancho 的观点,即 Cython 可能是要走的路,但这里有一些小的加速:

A.在开始 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 需要更少的工作,尽管它可能不会给你带来那么大的速度提升。我当然会先尝试。

【讨论】:

    【解决方案2】:

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

    http://wiki.cython.org/tutorials/numpy查看一个简短的教程/示例

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

    大多数 Python 代码已经是有效的 Cython,因此您可以使用现有的代码并在需要的地方添加注释和类型信息以加快速度。

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

    【讨论】:

      【解决方案3】:

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

      Scipy PerformancePython

      它有点过时了,但仍然很有帮助。请注意,它指的是 pyrex,正如 Sancho 所提到的,它已被分叉以创建 Cython 项目。

      我个人更喜欢 f2py,因为我认为 fortran 90 具有 numpy 的许多不错的功能(例如,一次操作将两个数组相加),但具有编译代码的全速。另一方面,如果您不了解 fortran,那么这可能不是正确的选择。

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

      【讨论】:

        猜你喜欢
        • 2016-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-08
        • 1970-01-01
        • 2019-05-02
        • 1970-01-01
        • 2020-09-16
        相关资源
        最近更新 更多