そうだPythonを勉強しよう(NumPy編)
http://d.hatena.ne.jp/yukichanko/20110215/1297784078
あいもかわらず、この続き。
とりあえず、NumPyを使う
Pure Python版で、
ey = [0.0] * (Const.NX+1) hz = [0.0] * Const.NX
としている定義を単にNumPy Arrayに置き換えます。
import numpy as np ey = np.array([0.0] * (Const.NX+1)) hz = np.array([0.0] * Const.NX)
メインのループはそのままです。
def calc_ey(ey, hz, dx, dt): """ 電界(Ey)更新 """ for i in range(1, Const.NX): ey[i] = ey[i] - dt / (Const.PERMITTIVITY * dx[i]) * (hz[i] - hz[i-1]) def calc_hz(hz, ey, dx, dt): """ 磁界(Hz)更新 """ for i in range(0, Const.NX): hz[i] = hz[i] - dt / (Const.PERMEABILITY * dx[i]) * (ey[i+1] - ey[i])
とすると、スピードはどうなるか。。。当然スピードはあがりません。
結果は、
言語 | CPU Time | Memory |
---|---|---|
C | 0.91sec(1.0) | 416KB |
Python(Pure List) | 72.9sec(80.1) | 3.29MB |
Python(NumPy Loop) | 476sec(523) | 7.86MB |
。。。えっ( ꒪⌓꒪) いや、予想していたとは言え、Pure ListとNumPy Loop版でこんなに遅くなるとは!!
正直、予想外。Pure Listと同等だと思っていました。話の流れとしては、単にリストをNumPy Arrayにしてもダメ。Pythonのループが遅いから、NumPy内のループに変えましょう、と言うのが話の流れなわけだけど、ここまで遅くなるというのは、Pythonのループの問題だけじゃないね。
PythonのループとNumPyのスカラアクセスが相性悪いのか、それともわざと非効率なインプリにして、ユーザーにアラートを与えているのか?
Pythonのループをなくす
ここまで遅いと、さすがにループの中でNumPyは使えません。このループをなくしましよう。どうするか。。。スライスを使用して、ベクトル演算に変えます。
def calc_ey(ey, hz, dx, dt): """ 電界(Ey)更新 """ ey[1:Const.NX] = ey[1:Const.NX] \ - dt / (Const.PERMITTIVITY * dx[1:Const.NX]) * (hz[1:Const.NX] - hz[0:Const.NX-1]) def calc_hz(hz, ey, dx, dt): """ 磁界(Hz)更新 """ hz[0:Const.NX] = hz[0:Const.NX] \ - dt / (Const.PERMEABILITY * dx[0:Const.NX]) * (ey[1:Const.NX+1] - ey[0:Const.NX])
はてさて、実力は。。。
言語 | CPU Time | Memory |
---|---|---|
C | 0.91sec(1.0) | 416KB |
Python(Pure List) | 72.9sec(80.1) | 3.29MB |
Python(NumPy Loop) | 476sec(523) | 7.86MB |
Python(NumPy Vector) | 5.53sec(6.07) | 7.90MB |
うん。だいぶん使えるぐらいになりました。まだ、Cとの差はありますが、それでもPythonのフレキシビリティを考えると、許容できるぐらいの速度差にはなったのではないでしょうか?
(NumPyを使ってもCほどは速くならないよ、と言う話を"Python Scripting for Computational Science"の中で見たような気がするのですが、線を引いてなかったので、どこかわからず。見つけたら、参照してみます。)
次は
SWIGを使って、電磁界更新部をCのコードに置き換えます。