簡體   English   中英

為什么我的循環的執行時間變化如此之大?

[英]Why does the execution time of my loop vary so much?

我有這個循環,我稍后會計算很多,所以我盡可能地優化它。 當我用一個相當大的文件(43564 個原子)運行它時,執行時間變化很大:從 4.5 秒到 18 秒。 我可以更改任何內容以更一致地達到 4.5 秒嗎?

std::vector<float> data(protein_atoms.size()*5);
for (size_t i = 0; i < protein_atoms.size(); i++) {
    const Atom& a = protein_atoms[i]; 
    data[5*i] = a.coords.x;
    data[5*i+1] = a.coords.y;
    data[5*i+2] = a.coords.z;
    data[5*i+3] = a.effective_charge;
    data[5*i+4] = a.occupancy;
}

cout << "Entering loop. Size: " << protein_atoms.size() << endl;
auto start = std::chrono::high_resolution_clock::now();
// calculate p-p distances
for (size_t i = 1; i < protein_atoms.size(); i++) {
    for (size_t j = i; j < protein_atoms.size(); j++) {
        double dist = sqrt(pow(data[5*i] - data[5*j], 2) + pow(data[5*i+1] - data[5*j+1], 2) + pow(data[5*i+2] - data[5*j+2], 2));
        double weight = data[5*i+3]*data[5*j+3]*data[5*i+4]*data[5*j+4]; // Z1*Z2*w1*w2
        p_pp[(int) dist/width] += 2*weight;
    }
}
p_pp[0] += protein_atoms.size();

auto stop = std::chrono::high_resolution_clock::now();
auto dur = std::chrono::duration_cast<std::chrono::milliseconds>(stop-start);
cout << "Loop took " << dur.count() << " milliseconds." << endl;

您可能會超出緩存大小,這可能會使其變慢。

以下是一些可以幫助加快速度的指針:

  • 通過預乘第 3 個和第 4 個元素,將每個原子 5 個浮點數減少到 4 個浮點數
  • 明確地做平方而不是 pow(x,2)
  • 對緩存更友好:分成 N 個塊(2 的小冪,比如 256)。 然后在塊上進行雙循環,並在雙循環內處理該塊對。 當然,您還需要考慮一個塊中的所有對,所以這是另一個單獨的雙 for 循環。

了解您正在使用的功能非常重要。 pow()使用對數計算功率(實際上類似於log(exp(x)*y) ,這是計算平方的一種非常低效的方法。如果pow function 未正確注冊為編譯器中的 intrinsec function (我不確定您是否會獲得此優化,但即使在這種情況下,將其更改為產品也過於復雜,無法確定它對編譯器會更好)無論如何,您正在更改( 1.384E9 * 2調用pow()將復雜的 function 計算為簡單的產品)這應該是一個很好的優化。

for (size_t i = 1; i < protein_atoms.size(); i++) {
    for (size_t j = i; j < protein_atoms.size(); j++) {
        double dist = sqrt(pow(data[5*i] - data[5*j], 2) + pow(data[5*i+1] - data[5*j+1], 2) + pow(data[5*i+2] - data[5*j+2], 2));
        double weight = data[5*i+3]*data[5*j+3]*data[5*i+4]*data[5*j+4]; // Z1*Z2*w1*w2
        p_pp[(int) dist/width] += 2*weight;
    }
}

一個好的優化編譯器將執行我在下面提出的建議,但基本思想是永遠不要重復兩次相同的計算(就像你展示的5*i5*j一樣)而且知道你在平方是不好的使用pow()因為它需要對數來做冪(最好做一個簡單的x*x ,而不是pow(x, 2) )。 我應該以這種方式編寫該代碼:

size_t sz = protein_atoms.size(); // this will save a lot of size() 
                                  // calculations to get the same result
                                  // (1,384E9 times) in case the size()
                                  // method is not inlined.
for (size_t i = 1; i < sz; i++) {
    for (size_t j = i; j < sz; j++) {
        int i5 = 5*i, j5 = 5*j;
        double d0 = data[i5  ] - data[j5  ],
               d1 = data[i5+1] - data[j5+1],
               d2 = data[i5+2] - data[j5+2],
               dist = d0*d0 + d1*d1 + d2*d2,
               d3 = data[i5+3] - data[j5+3],
               d4 = data[i5+4] - data[j5+4],
               weight2 = 2*d3*d3*d4*d4; // included the 2 factor here.
        p_pp[(int) dist/width] += weight2;
    }
}

順便說一句,我不明白您為什么將dist的值轉換為int ,因為在除以width以計算p_pp索引時,它將轉換為double 你不應該是指(int) dist / (int) width嗎? (使 integer 划分)或相反,你的意思是(int)(dist / width) (將結果轉換為int --- 不必要,因為它是由語言完成的)請記住(int)/具有更高的優先級。

最后,我假設你寫的是你想做的,但我不確定你在每次循環迭代時將ij加一是否做得好。 我更傾向於每次都添加5 ,使用i += 5j += 5之類的表達式。

一個好的編譯器應該自己做所有這些優化(可能更多,比如以某種方式展開循環,以避免跳轉),你可能可以通過多線程來獲得更快的速度(一些編譯器這樣做)並使循環迭代由不同的線程完成,因此您可以受益於擁有多個計算核心。

最后說明

習慣於發布一個最小的、可重現的示例,因為我無法測試您的代碼。 這非常重要,因為我自己無法重現(和衡量)節省的時間,因為您的代碼無法在本地執行以進行測試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM