2010-09-15 121 views
2

我有一個程序比我想要的運行速度慢。這可以優化嗎?

我已經做了一些分析,我發現是絕大多數的處理時間,佔用了部分

 DO K = 0, K_MAX 
      WRITE(EIGENVALUES_IO, *) K * 0.001 * PI, (W_UP(J), J=1, ATOM_COUNT) 
      DCMPLXW_UP(:) = DCMPLX(W_UP(:)) 
      DO E = 1, ENERGY_STEPS 
       ENERGY = MIN_ENERGY + ENERGY_STEP * REAL(E, DP) 
       ZV = DCMPLX(ENERGY, DELTA) 
       ON_SITE_SINGLE = DCMPLX(0.0_DP) 
       DO Q = 1, ATOM_COUNT 
        DO J = 1, ATOM_COUNT 
         ON_SITE_SINGLE(J) = ON_SITE_SINGLE(J) + (MATRIX_UP(J, Q) * MATRIX_UP_CONJG(J, Q))/(ZV - DCMPLXW_UP(Q)) 
        END DO 
       END DO 
       DOS_DOWN(E) = DOS_DOWN(E) - WEIGHTS(K) * SUM(IMAG(ON_SITE_SINGLE)) 
      END DO 
     END DO 

ON_SITE_SINGLE(J) = ON_SITE_SINGLE(J) + (MATRIX_UP(J, Q) * MATRIX_UP_CONJG(J, Q))/(ZV - DCMPLXW_UP(Q)) 

是做一個傷害。

我是這方面的新手,有沒有加速這方面的一些方法? AFAIK,同樣的原則適用於C,所以你們的任何幫助也會很好。

該陣列所有複雜

K_MAX是1000個

ENERGY_STEPS是1000

ATOM_COUNT低(< 50)

+0

您會希望該行佔用大部分時間,因爲每執行一次該語句執行次數最多爲2500次。除數可以在內循環之外計算。這條線將一直佔用,但總數將減少。 – 2010-09-15 13:01:34

回答

5

我的所有程序都比我想要的慢。在所有的(不是全部,但很多)我的科學計劃中都有一個深層循環嵌套,其中最內層的語句佔用了大部分計算時間。通常情況下,我希望我的計算中有90%能夠被這些語句所採用。你最內在的陳述正在執行2.5x10^9次,所以你應該期望它佔用總時間的很大一部分時間。

牢記這一點,我建議你:

a)採取@亞歷山大的建議使用BLAS,而不是你的家庭自釀矩陣向量乘法。

b)忽略@ Yuval的關於將操作提取出循環的建議 - 如果將優化級別調高,一個好的Fortran編譯器會爲您執行此操作(警告:這是一個自我實現的預言,就像編譯器一樣這不是一個好的)。參見(d),我期望從Fortran中獲得很多其他優化。 (我不希望編譯器對內存訪問進行優化,我期望從BLAS中獲益)。

c)形成一個現實的期望,即您應該從程序中獲得多少性能。如果持續的FLOPs率超過CPU額定性能的10%,那麼你做得很好,應該花時間做其他事情而不是優化。

d)仔細閱讀你的編譯器文檔。確保您瞭解優化標誌實際執行的操作。確保你正在爲你正在使用的CPU生成代碼,而不是一些舊版本。如果可用,則切換到快速矢量操作。所有這些事情。 e)開始並行化。 OpenMP是一個開始的好地方,正如@Nicolas所說的,開始時學習曲線非常平緩。

哦,您似乎遵循的建議0是測量代碼的性能並測量您所做的任何更改的影響。

+0

謝謝,非常感謝 – 2010-09-15 12:41:09

+0

+1非常好的建議(第一句話讓我大笑:-) – Rook 2010-09-15 17:39:58

+0

小心比較Fortran中優化級別的輸出。編譯器的積極優化可以顯着改變循環中的計算結果。 – 2010-10-06 21:15:22

1

的因素你除以,即

(ZV - DCMPLXW_UP(Q)) 

不依賴於J,只能在Q. 因此,我會將此計算移至Q循環。 更好的是,計算:

1/(ZV - DCMPLXW_UP(Q)) 

在外環,並通過將其乘代替環 內部分割(AFAIR,乘法比司更快)。 另外,請檢查您的矩陣數據結構是否與循環相對應(即儘可能循環遍歷內存的連續部分)。通常,如果您可以改進算法,這將是最大的運行時間改進。

Programming Pearls對類似的優化有很好的描述。

1

如果常規代碼優化讓你陷入困境,你可以試試OpenMP,這是一個用於C和Fortran的並行編程的API。在代碼循環之前插入一些指令,即「預編譯器」樣式,它將在不同的進程之間分割出大量的循環。

您有幾條可能想要嘗試的說明。例如:

#pragma omp parallel for 
/* Loop here */ 

這是一個非常完整的API,您可以根據大量參數,共享變量和不同的並行分割技術來分割所有內容。您也可以指定您希望OpenMP創建的進程數量,核心數量等。

通過稍微調整,您將最終找到一個提高計算速度的解決方案。

+0

我想避免並行的事情,但是,謝謝,不知道這樣做會非常簡單,做循環 – 2010-09-15 12:21:00

+0

當然沒有問題,但如果你想在未來嘗試它,那麼它是!令我印象深刻的是:對於矩陣中的大型計算,與我的常規C代碼相比,速度真的提高了。 – 2010-09-15 12:29:00

1

請將BLAS用於'vactor plus matrix-vector multiplies'。你基本上是這樣做的

ON_SITE_SINGLE(J) = ON_SITE_SINGLE(J) + (MATRIX_UP(J, Q) * MATRIX_UP_CONJG(J, Q))/(ZV - DCMPLXW_UP(Q)) 

有良好的調整BLAS庫,你可以有顯着的改善。

+0

我已經在這個程序的其他地方使用了BLAS,但是就像我說的,我對它相當陌生 - 有關我需要使用哪個例程的任何提示? – 2010-09-15 12:36:25

+0

* gemv,可能是dgemv。另一件事是將MATRIX_UP * MATRIX_UP_CONJG預先計算成一個矩陣,1 /(ZV - DCMPLXW_UP)併入一個向量,然後調用* GEMV。 – 2010-09-15 14:20:13