3

我试图弄清楚为什么我的一个 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 vs. 6.3s for Python?这是我第一次担心性能问题,但正如我所说,在我试图加速的代码中,使用 Python 只能达到 gfortran 速度的四分之一左右。

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

除非我遗漏了什么,否则我不同意最后的说法:我必须在 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
4

1 回答 1

5

首先,python 代码不会生成与 fortran 代码相同的输出。在 fortran 程序中,y 是从 0 到 39 的序列,然后是十个 0,十个 1,...,一直到十个 39。python 代码输出 11 个 0、11 个 1 到 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.2 秒内运行,上述 python 在 8.6 秒内运行。

但是,这不是一个公平的比较:对于 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.3 秒(同样是 1,000,000 次迭代)。所以已经大约。快 25%。

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

于 2013-01-28T01:12:32.990 回答