[英]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;
您可能會超出緩存大小,這可能會使其變慢。
以下是一些可以幫助加快速度的指針:
了解您正在使用的功能非常重要。 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*i
或5*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)
比/
具有更高的優先級。
最后,我假設你寫的是你想做的,但我不確定你在每次循環迭代時將i
和j
加一是否做得好。 我更傾向於每次都添加5
,使用i += 5
和j += 5
之類的表達式。
一個好的編譯器應該自己做所有這些優化(可能更多,比如以某種方式展開循環,以避免跳轉),你可能可以通過多線程來獲得更快的速度(一些編譯器這樣做)並使循環迭代由不同的線程完成,因此您可以受益於擁有多個計算核心。
習慣於發布一個最小的、可重現的示例,因為我無法測試您的代碼。 這非常重要,因為我自己無法重現(和衡量)節省的時間,因為您的代碼無法在本地執行以進行測試。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.