如何在这个短代码中提高numpy性能?

时间:2013-01-27 23:00:05

标签: python performance numpy fortran

我试图了解为什么我的一个python脚本比gfortran慢了大约4倍,而且我必须这样做:

import numpy as np

nvar_x=40
nvar_y=10

def fn_tst(x):
    for i in range(int(1e7)):
        y=np.repeat(x,1+nvar_y)
    return y

x = np.arange(40)
y = fn_tst(x)

print y.min(),y.max()

这比以下fortran代码慢约13倍

module test
integer,parameter::nvar_x=40,nvar_y=10
contains
subroutine fn_tst(x,y)
real,dimension(nvar_x)::x
real,dimension(nvar_x*(1+nvar_y))::y

do i = 1,10000000
   do k = 1,nvar_x
      y(k)=x(k)
      ibeg=nvar_x+(k-1)*nvar_y+1
      iend=ibeg+nvar_y-1
      y(ibeg:iend)=x(k)
   enddo
enddo

end subroutine fn_tst
end module test

program tst_cp
use test
real,dimension(nvar_x)::x
real,dimension(nvar_x*(1+nvar_y))::y
do k = 1,nvar_x
   x(k)=k-1
enddo

call fn_tst(x,y)

print *,minval(y),maxval(y)

stop
end

你能否提出加速python脚本的方法。其他指向numpy良好性能的指针将不胜感激。我宁愿坚持使用python而不是为fortran例程构建python包装器。

谢谢

@isedev,是的,就是这样。 1.2s gfortran与6.3s for Python?这是我第一次担心性能,但正如我所说,在我试图加速的代码中,我只能使用Python获得大约四分之一的速度。

对,对不起,代码没有做同样的事情。实际上,您在循环中指示的内容更像我在原始代码中所拥有的内容。

除非我遗漏了什么,否则我不同意最后的陈述:我必须在fn_tst中创建y。和np.repeat只是RHS上的一个术语(将o / p直接放在现有数组中)。如果我注释掉np.repeat术语的话很快......

rhs_slow = rhs[:J]
rhs_fast = rhs[J:]

rhs_fast[:] = c* ( b*in2[3:-1] * ( in2[1:-3] - in2[4:]  ) - fast) + hc_ovr_b * np.repeat(slow,K) #slow

1 个答案:

答案 0 :(得分:5)

首先,python代码不会生成与fortran代码相同的输出。在fortran程序中,y是0到39的序列,接着是10个0,11个,......,一直到10个39。 python代码输出11个0,11个,一直到11个39个。

此代码生成相同的输出,并执行与原始代码相同数量的内存分配:

import numpy as np

nvar_x = 40
nvar_y = 10

def fn_tst(x):
    for i in range(10000000):
        y = np.empty(nvar_x*(1+nvar_y))
        y[0:nvar_x] = x[0:nvar_x]
        y[nvar_x:] = np.repeat(x,nvar_y)
    return y

x = np.arange(40)
fn_tst(x)

print y.min(), y.max()

在我的系统上(仅限1,000,000个循环),fortran代码在1.2s内运行,上面的python在8.6s内运行。

然而,这不是一个公平的比较:对于fortran代码,y被分配一次(在fn_tst例程之外)并且使用python代码,y在fn_tst函数内分配。

因此,如下重写Python代码可以提供更好的比较:

import numpy as np

nvar_x = 40
nvar_y = 10

def fn_tst(x,y):
    for i in range(10000000):
        y[0:nvar_x] = x[0:nvar_x]
        y[nvar_x:] = np.repeat(x,nvar_y)
    return y

x = np.arange(40)
y = np.empty(nvar_x*(1+nvar_y))
fn_tst(x,y)

print y.min(), y.max()

在我的系统上,上面运行的是6.3s(再次,1,000,000次迭代)。所以已经约快25%。

在这种情况下,主要的性能是numpy.repeat()生成一个数组,然后需要将其复制回y。如果可以指示numpy.repeat()将其输出直接放在现有数组中(即在这种情况下为y),事情就会快得多......但这似乎不可能。