9

F2PYを使用してFortran90サブルーチンをPythonにラップしました。ここでの微妙な点は、Fortranサブルーチンasloが引数の1つとしてPythonコールバック関数を使用することです。

SUBROUTINE f90foo(pyfunc, a)
real(kind=8),intent(in) :: a
!f2py intent(callback) pyfunc
external pyfunc
!f2py real*8 y,x
!f2py y = pyfunc(x)

!*** debug begins***
print *, 'Start Loop'
do i=1,1000
  p = pyfunc(a)
end do
total = etime(elapsed)
print *, 'End: total=', total, ' user=', elapsed(1), ' system=', elapsed(2)
stop
!*** debug ends  ***

これpyfuncは私のPythonコードの他の場所で定義されているPython関数です。ラッパーは正常に動作しますが、上記のラップされたバージョンを実行すると、次のように純粋なPythonを使用して取得できる経過時間の約5倍の経過時間が得られました。

def pythonfoo(k):
    """ k: scalar 
        returns: scalar
    """
    print('Pure Python: Start Loop')
    start = time.time()
    for i in xrange(1000):
        p = pyfunc(k)
    elapsed = (time.time() - start)
    print('End: total=%20f'% elapsed)

それで、問題は、オーバーヘッドは何から来るのかということです。pyfunc純粋なFortran関数に再コーディングするのは非常に時間がかかるので、そのままにしておきたいのですが、ラッパーモジュールの速度を向上させる方法はありますか?

4

1 に答える 1

9

投稿したコードでaは、倍精度浮動小数点数です。これをFortranからPythonに渡すということは、FortranのdoubleをPyFloatオブジェクトにラップすることを意味しますが、これにはコストがかかります。純粋なPythonバージョンでは、kはPyFloatであり、1000回ラップするための代金を支払う必要はありません。

もう1つの問題は、関数呼び出し自体です。CからPython関数を呼び出すことは、パフォーマンスの面ではすでに悪いことですが、Fortran関数呼び出し規約(スタックなどに関する)をC関数呼び出し規約に変換するコードの追加レイヤーがあるため、Fortranから呼び出すことはさらに悪いです。CからPython関数を呼び出す場合は、引数をPythonオブジェクトとして準備する必要があります。通常、Python関数の* args引数として機能するPyTupleオブジェクトを作成し、モジュールのテーブルを検索して関数ポインターを取得します。 ..

最後になりましたが、FortranとNumpyの間で2D配列を渡すときは、配列の順序に注意する必要があります。F2pyとnumpyはその点で賢い場合がありますが、PythonコードがFortranの順序で配列を操作するように記述されていない場合、パフォーマンスが低下します。

pyfuncの意味はわかりませんが、投稿した内容に近い場合は、Pythonでループを記述し、関数を1回だけ呼び出すと時間を節約できます。また、中間値()が必要な場合はp、Python関数がすべての中間値を含むNumpy配列を返すようにします。

于 2011-09-23T06:27:11.177 に答える