我在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
答案 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,所以你可以只使用你拥有的东西,并在需要的地方添加注释和输入信息,以便为你提供一些加速。
我怀疑您可以充分利用索引x
,y
,orig_x
和numpy数组添加类型信息。
答案 2 :(得分:1)
以下指南比较了几种在python中优化数字代码的不同方法:
有点过时,但仍然有用。请注意,它引用了pyrex,它已被分叉以创建Cython项目,如Sancho所述。
我个人更喜欢f2py,因为我认为fortran 90具有许多numpy的优点(例如,将两个数组与一个操作一起添加),但具有编译代码的全速。另一方面,如果你不了解fortran,那么这可能就不是了。
我简单地尝试了cython,我发现的麻烦是默认情况下cython生成的代码可以处理任意python类型,但仍然很慢。然后你必须花时间添加所有必要的cython声明,以使它更具体和快速,而如果你使用C或fortran,那么你将倾向于直接获得快速代码。同样,我已经熟悉这些语言,这是有偏见的,而如果Python是你所知道的唯一语言,Cython可能更合适。