2016-09-02 19 views
0

這裏是我的代碼如何加速嵌入了numpy功能的python代碼?

def timepropagate(wv1, ham11, 
        ham12, ham22, scalararray, nt): 
    wv2 = np.zeros((nx, ny), 'c16') 
    fw1 = np.zeros((nx, ny), 'c16') 
    fw2 = np.zeros((nx, ny), 'c16') 
    for t in range(0, nt, 1): 
     wv1, wv2 = scalararray*wv1, scalararray*wv2 
     fw1, fw2 = (np.fft.fft2(wv1), np.fft.fft2(wv2)) 
     fw1 = ham11*fw1+ham12*fw2 
     fw2 = ham12*fw1+ham22*fw2 
     wv1, wv2 = (np.fft.ifft2(fw1), np.fft.ifft2(fw2)) 
     wv1, wv2 = scalararray*wv1, scalararray*wv2 
    del(fw1) 
    del(fw2) 
    return np.array([wv1, wv2]) 

流功能的速度是什麼,我需要做的是找到一個合理的快速實現,讓我去的速度的兩倍,最好最快的。

我感興趣的更一般的問題是如何使用最小的可能的連接回到python來加速這一塊。我假設,即使我加速了代碼的特定部分,比如說標量數組乘法,我仍然會回過頭來從python中進行傅立葉變換,這需要時間。有沒有什麼方法可以使用,比如說numba或者cython,而不是在循環中間讓這個「迴歸」python? 就個人而言,考慮到我已經在使用其他線程,我寧願在單線程中快速執行某些操作。

編輯:這裏是分析的結果,第一個是4096×4096陣列的10個時間步長,我需要將其放大爲新臺幣= 8000

ncalls tottime percall cumtime percall filename:lineno(function) 
    1 0.099 0.099 432.556 432.556 <string>:1(<module>) 
    40 0.031 0.001 28.792 0.720 fftpack.py:100(fft) 
    40 45.867 1.147 68.055 1.701 fftpack.py:195(ifft) 
    80 0.236 0.003 47.647 0.596 fftpack.py:46(_raw_fft) 
    40 0.102 0.003 1.260 0.032 fftpack.py:598(_cook_nd_args) 
    40 1.615 0.040 99.774 2.494 fftpack.py:617(_raw_fftnd) 
    20 0.225 0.011 29.739 1.487 fftpack.py:819(fft2) 
    20 2.252 0.113 72.512 3.626 fftpack.py:908(ifft2) 
    80 0.000 0.000 0.000 0.000 fftpack.py:93(_unitary) 
    40 0.631 0.016 0.820 0.021 fromnumeric.py:43(_wrapit) 
    80 0.009 0.000 0.009 0.000 fromnumeric.py:457(swapaxes) 
    40 0.338 0.008 1.158 0.029 fromnumeric.py:56(take) 
    200 0.064 0.000 0.219 0.001 numeric.py:414(asarray) 
    1 329.728 329.728 432.458 432.458 profiling.py:86(timepropagate) 
    1 0.036 0.036 432.592 432.592 {built-in method builtins.exec} 
    40 0.001 0.000 0.001 0.000 {built-in method builtins.getattr} 
    120 0.000 0.000 0.000 0.000 {built-in method builtins.len} 
    241 3.930 0.016 3.930 0.016 {built-in method numpy.core.multiarray.array} 
    3 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.zeros} 
    40 18.861 0.472 18.861 0.472 {built-in method numpy.fft.fftpack_lite.cfftb} 
    40 28.539 0.713 28.539 0.713 {built-in method numpy.fft.fftpack_lite.cfftf} 
    1 0.000 0.000 0.000 0.000 {built-in method numpy.fft.fftpack_lite.cffti} 
    80 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 
    40 0.006 0.000 0.006 0.000 {method 'astype' of 'numpy.ndarray' objects} 
    1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
    80 0.000 0.000 0.000 0.000 {method 'pop' of 'list' objects} 
    40 0.000 0.000 0.000 0.000 {method 'reverse' of 'list' objects} 
    80 0.000 0.000 0.000 0.000 {method 'setdefault' of 'dict' objects} 
    80 0.001 0.000 0.001 0.000 {method 'swapaxes' of 'numpy.ndarray' objects} 
    40 0.022 0.001 0.022 0.001 {method 'take' of 'numpy.ndarray' objects} 

我覺得我做錯了的第一次使用time.time()來計算小數組的時間差,並推斷較大數組的結論。

+5

優化的第一條規則是分析函數。哪部分是瓶頸?如果是fft,你可能無法期待任何顯着的加速。 – MSeifert

+3

在這種情況下,「hamij」是什麼?是否有可能利用卷積定理並在時域而不是頻域中執行操作?正如上述評論者所說,我認爲你不可能比現在更快地做出更快的速度。 – eigenchris

+0

你會想看看'fft'包,但是我認爲,在一次或python調用之後,它最終會使用一個編譯過的庫。 – hpaulj

回答

0

如果大部分時間都花在哈密爾頓乘法上,那麼您可能需要在該部分應用numba。如果從NumPy中評估表達式,移除所需的所有臨時數組將會帶來最大的好處。請記住,數組(4096,4096,c16)足夠大,不能很好地適應處理器緩存中的數組。單個矩陣需要256 MiB。所以認爲性能不太可能與操作有關,而是取決於帶寬。因此,以一種只在輸入操作數中執行一次傳遞的方式來實現這些操作。這在numba中實現是非常微不足道的。注意:您只需要在numba中實施哈密爾頓表達式。

我還要指出的是,「preallocations」使用np.zeros似乎發出信號,你的代碼是不是按照你的意圖是:

fw1 = ham11*fw1+ham12*fw2 
    fw2 = ham12*fw1+ham22*fw2 

將實際用於FW1創建新的陣列,FW2。如果你的意圖是重用緩衝區,你可能想使用「fw1 [:,:] = ...」。否則,np.zeros只會浪費時間和內存。

您可能想要考慮將(wv1,wv2)連接到(2,4096,4096,c16)數組中。與(fw1,fw2)相同。這樣,代碼會更簡單,因爲您可以依靠廣播來處理「scalararray」產品。 fft2和ifft2實際上會做正確的事情(AFAIK)。