2012-05-24 48 views
0

我正試圖在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。

+0

什麼類型是shared_array?計算值是什麼類型? –

+0

共享數組是雙精度數組,也是計算值(它們在生成時存儲在本地私有雙精度向量中) – Alex

+0

我沒有看到任何對'malloc'或'free'的調用,它是你看到的任何堆損壞的關鍵... –

回答

1

因此,有這樣的人沒有能回答這一點,但已經想通了,我希望這可以幫助別人,給定系統的行爲是如此離奇。

之一(C)的功能,我最終打電話到(my_function - >intermediate_function - >lower_function - >BAD_FUNCTION)宣佈了一些它的變量爲static,這意味着它們保持相同的內存地址等基本扮演一個共享變量。有趣的是,靜態覆蓋OpenMP。

我發現了這一切;

  • 使用Valgrid來確定發生錯誤的位置,並查看所涉及的特定變量。

  • 將整個for-loop定義爲關鍵部分,然後在頂部和底部公開更多代碼。

  • 與我的老闆交談。更多的眼睛總是有幫助的,尤其是因爲你不得不口頭表達問題(最終以我打開罪魁禍首功能並指向聲明)

+2

'static'不會覆蓋OpenMP - 這是預期的行爲。 OpenMP標準(當前版本3.0)對於靜態變量非常明確:在第2.9.1.1節「在結構中的範圍內聲明的靜態變量是共享的」。在第2.9.1.2節中:「在該區域中被調用的例程中聲明的靜態變量是共享的。」 –

+0

對不起 - 我應該明確表示我並不是說這是一個錯誤,或者確實是意想不到的。它只是(愚蠢)意外的*我* - 雖然你可以配置OpenMP在不同的內存位置爲每個線程創建靜態變量。然而,你生活和學習(特別是,檢查文檔 - 感謝參考!) – Alex