簡體   English   中英

混合 OpenMP 和 xmmintrin SSE Intrinsics - 在非並行版本上沒有得到加速

[英]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 中,並且在並行區域之外,初始線程提取minimumminimumNode

        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.

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