[英]Mixing OpenMP and xmmintrin SSE Intrinsics - not getting speedup over the non-parallel version
我已經使用xmmintrin.h
SSE 指令實現了旅行推銷員的一個版本,獲得了不錯的加速。 但現在我也在嘗試在它之上實現 OpenMP 線程,而且我看到速度急劇下降。 我在這兩種情況下都得到了正確的答案(即(i)僅使用 SSE,或(ii)使用 SSE && OpenMP)。
我知道我可能做錯了什么,也許比我更有經驗的人可以發現這個問題。
我的程序的主循環有以下(簡短的)偽代碼:
int currentNode;
for(int i = 0; i < numNodes; i++) {
minimumDistance = DBL_MAX;
minimumDistanceNode;
for(int j = 0; j < numNodes; j++) {
// find distance between 'currentNode' to j-th node
// ...
if(jthNodeDistance < minimumDistance) {
minimumDistance = jthNodeDistance;
minimumDistanceNode = jthNode;
}
}
currentNode = minimumDistanceNode;
}
這是我的實現,這仍然是半偽代碼,因為我仍然刷過一些我認為不會影響性能的部分,我認為我的代碼要發現的問題可以在以下代碼中找到片段。 如果您只是省略#pragma
行,那么以下內容與同一程序的僅 SSE 版本幾乎相同,所以我認為我應該只包括 OpenMP 版本:
int currentNode = 0;
#pragma omp parallel
{
#pragma omp single
{
for (int i = 1; i < totalNum; i++) {
miniumum = DBL_MAX;
__m128 currentNodeX = _mm_set1_ps(xCoordinates[currentNode]);
__m128 currentNodeY = _mm_set1_ps(yCoordinates[currentNode]);
#pragma omp parallel num_threads(omp_get_max_threads())
{
float localMinimum = DBL_MAX;
float localMinimumNode;
#pragma omp for
for (int j = 0; j < loopEnd; j += 4) {
// a number of SSE vector calculations to find distance
// between the current node and the four nodes we're looking
// at in this iteration of the loop:
__m128 subXs_0 = _mm_sub_ps(currentNodeX, _mm_load_ps(&xCoordinates[j]));
__m128 squareSubXs_0 = _mm_mul_ps(subXs_0, subXs_0);
__m128 subYs_0 = _mm_sub_ps(currentNodeY, _mm_load_ps(&yCoordinates[j]));
__m128 squareSubYs_0 = _mm_mul_ps(subYs_0, subYs_0);
__m128 addXY_0 = _mm_add_ps(squareSubXs_0, squareSubYs_0);
float temp[unroll];
_mm_store_ps(&temp[0], addXY_0);
// skipping stuff here that is about getting the minimum distance and
// it's equivalent node, don't think it's massively relevant but
// each thread will have its own
// localMinimum
// localMinimumNode
}
// updating the global minimumNode in a thread-safe way
#pragma omp critical (update_minimum)
{
if (localMinimum < minimum) {
minimum = localMinimum;
minimumNode = localMinimumNode;
}
}
}
// within the 'omp single'
ThisPt = minimumNode;
}
}
}
所以我的邏輯是:
omp single
用於頂層 for(int i) for 循環,我只想要 1 個線程專用於此omp parallel num_threads(omp_get_max_threads())
用於內部 for(int j) for 循環,因為我希望所有內核同時處理這部分代碼。omp critical
,因為我想線程安全地更新當前節點。在運行時間方面,OpenMP 版本的速度通常是 SSE-only 版本的兩倍。
在我的代碼中是否有什么特別糟糕的地方突然出現,導致 OpenMP 的速度急劇下降?
在我的代碼中是否有什么特別糟糕的地方突然出現,導致 OpenMP 的速度急劇下降?
第一的:
omp single 用於頂層 for(int i) for 循環,我只想要 1 個線程專用於此
在您的代碼中,您具有以下內容:
#pragma omp parallel
{
#pragma omp single
{
for (int i = 1; i < totalNum; i++)
{
#pragma omp parallel num_threads(omp_get_max_threads())
{
//....
}
// within the 'omp single'
ThisPt = minimumNode;
}
}
}
#pragma omp parallel
創建了一組線程,但隨后只有一個線程執行並行任務(即#pragma omp single
),而其他線程不執行任何操作。 您可以簡化為:
for (int i = 1; i < totalNum; i++)
{
#pragma omp parallel num_threads(omp_get_max_threads())
{
//....
}
ThisPt = minimumNode;
}
內部僅由一個線程執行。
第二:
omp parallel num_threads(omp_get_max_threads()) 用於內部 for(int j) for 循環,因為我希望所有內核同時處理這部分代碼。
問題是這可能會返回邏輯內核的數量而不是物理內核的數量,並且某些代碼在超線程中的性能可能會更差。 因此,我將首先使用不同數量的線程進行測試,從 2、4 等開始,直到找到代碼停止縮放的數字。
omp 在完整的 for(int j) 循環結束時很關鍵,因為我想線程安全地更新當前節點。
// updating the global minimumNode in a thread-safe way
#pragma omp critical (update_minimum)
{
if (localMinimum < minimum) {
minimum = localMinimum;
minimumNode = localMinimumNode;
}
}
這可以通過創建一個數組來替換,其中每個線程將其局部最小值保存在為該線程保留的 position 中,並且在並行區域之外,初始線程提取minimum
和minimumNode
:
int total_threads = /..;
float localMinimum[total_threads] = {DBL_MAX};
float localMinimumNode[total_threads] = {DBL_MAX};
#pragma omp parallel num_threads(total_threads)
{
/...
}
for(int i = 0; i < total_threads; i++){
if (localMinimum[i] < minimum) {
minimum = localMinimum[i];
minimumNode = localMinimumNode[i];
}
}
最后,在完成這些更改后,您嘗試檢查是否可以通過以下方式替換此並行化:
#pragma omp parallel for
for (int i = 1; i < totalNum; i++)
{
...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.