我正試圖在OpenMP中並行化一個相當龐大的for-loop
。大約20%的時間它運行良好,但其餘時間它與各種segfaults崩潰,如;OpenMP會導致heisenbug段錯誤
*** glibc detected *** ./execute: double free or corruption (!prev): <address> ***
*** glibc detected *** ./execute: free(): invalid next size (fast): <address> ***
[2] <PID> segmentation fault ./execute
我的通用代碼結構如下;
<declare and initialize shared variables here>
#pragma omp parallel private(list of private variables which are initialized in for loop) shared(much shorter list of shared variables)
{
#pragma omp for
for (index = 0 ; index < end ; index++) {
// Lots of functionality (science!)
// Calls to other deep functions which manipulate private variables
// Finally generated some calculated_values
shared_array1[index] = calculated_value1;
shared_array2[index] = calculated_value2;
shared_array3[index] = calculated_value3;
} // end for
}
// final tidy up
}
在發生的事情而言,每次循環迭代是完全相互獨立的循環反覆,除了他們拉的數據從共享矩陣(但不同的列在每個循環迭代)的事實。在我調用其他函數的地方,它們只是改變私有變量(雖然偶爾會讀取共享變量),所以我認爲它們會是線程安全的,因爲它們只是將特定線程的本地東西搞亂了。任何共享變量的唯一寫法發生在最後,我們將各種計算值寫入某些共享數組,其中數組元素由for-loop索引編制索引。該代碼使用C++,但它調用的代碼是C和C++代碼。
我一直在試圖找出問題的根源,但至今沒有運氣。如果我設置num_theads(1)它運行良好,因爲它確實,如果我包圍for-loop
的內容在單個
#pragma omp for
for(index = 0 ; index < end ; index++) {
#pragma omp critical(whole_loop)
{
// loop body
}
}
這大概給出了相同的效果(即,只有一個線程可以通過在任何一個環一次)。
另一方面,如果我將for-loop's
的內容附在兩個critical
指令中,例如,
#pragma omp for
for(index = 0 ; index < end ; index++) {
#pragma omp critical(whole_loop)
{
// first half of loop body
}
#pragma omp critical(whole_loop2)
{
// second half of loop body
}
}
我得到了不可預測的短期違約。同樣,如果我在critical
指令中包含EVERY函數調用,它仍然不起作用。
我認爲這個問題可以被鏈接到一個函數調用的原因是因爲當我Valgrind(使用valgrind --tool=drd --check-stack-var=yes --read-var-info=yes ./execute
)以及SIGSEGing配置文件,我得到一個瘋狂數量加載和存儲的錯誤,如;
Conflicting load by thread 2 at <address> size <number>
at <address> : function which is ultimately called from within my for loop
根據the valgrind manual這是你與比賽條件期望是什麼。當然,這種奇怪的出現/消失問題似乎與競爭條件會導致的非確定性錯誤的種類一致,但我不明白如何每個調用都會產生顯而易見的競爭條件。
事情可能是錯的,但我不認爲是包括;
所有私人()變量在
for-loops
內初始化(因爲它們是線程局部)。我已檢查共享變量具有相同的內存地址,而私有變量具有不同的內存地址。
我不知道同步會有所幫助,但鑑於上有入口和出口
critical
指令隱含barrier
指令,我已經試過,每一個函數調用封裝在一個(唯一命名的)關鍵的我的代碼版本我認爲我們可以排除這一點。
如何以最佳方式進行將非常感激的任何想法。我一整天都在不停地抨擊我的頭。很顯然,我不是在尋找一個「哦,這是問題」類型的答案,而是更多地在調試/解構方面繼續前進。
可能是問題或可能有幫助的事情;
代碼中有一些std :: Vectors使用vector.pushback()函數添加元素。我記得讀過調整大小矢量不是線程安全的,但矢量只是私有變量,所以不在線程之間共享。我覺得這樣可以嗎?
如果我包圍整個
for-loop
體在critical
指令,並慢慢地縮了回去的代碼塊的結束(因此在for-loop
結束日益增長的區域是關鍵的部分外),直到我露出一個運行良好的函數調用,此時會重新開始segfaulting。用Valgrind分析這個二進制文件顯示了許多其他函數調用中的競爭條件,而不僅僅是我公開的一個。其中一個函數調用是根據Valgrind不會觸發任何競爭條件的GSL函數。
我是否需要在被調用的函數中明確定義私有變量和共享變量?如果是這樣,這對於OpenMP似乎有些限制 - 這是否意味着您需要對所調用的任何遺留代碼具有OpenMP兼容性?
並行化一個大的
for-loop
只是不是有用的東西?如果你已經讀了這麼多,謝謝你和Godspeed。
什麼類型是shared_array?計算值是什麼類型? –
共享數組是雙精度數組,也是計算值(它們在生成時存儲在本地私有雙精度向量中) – Alex
我沒有看到任何對'malloc'或'free'的調用,它是你看到的任何堆損壞的關鍵... –