3

私のpythonスクリプトの1つが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 を使用した優れたパフォーマンスへの他の指針もいただければ幸いです。Fortran ルーチン用の Python ラッパーをビルドするよりも、Python に固執したいと思います。

ありがとう

@isedev、それで、これですか。1.2s gfortran 対 Python の 6.3s? パフォーマンスについて心配したのはこれが初めてですが、前述したように、高速化しようとしていたコードで Python を使用すると、gfortran の約 4 分の 1 の速度しか得られませんでした。

そうです、申し訳ありませんが、コードは同じことをしていませんでした。実際、ループで示していることは、元のコードにあるものに似ています。

何かが欠けていない限り、最後のステートメントに同意しません: fn_tst で y を作成する必要があります。np.repeat は RHS の用語の 1 つにすぎません (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 までのシーケンスで、その後に 10 個の 0、10 個の 1、...、10 個の 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 に答える