首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >混合OpenMP和xmmintrin SSE Intrinsics -没有在非并行版本上获得加速比

混合OpenMP和xmmintrin SSE Intrinsics -没有在非并行版本上获得加速比
EN

Stack Overflow用户
提问于 2021-04-06 01:07:34
回答 1查看 119关注 0票数 1

我用旅行推销员的SSE指令实现了xmmintrin.h的一个版本,得到了一个不错的加速。但是现在我也在尝试在其之上实现OpenMP线程,并且我看到了相当大的速度减慢。在这两种情况下,我都得到了正确的答案(即(i)只使用SSE,或者(ii)使用SSE && OpenMP)。

我知道我可能做错了什么,也许比我更有经验的人能发现问题。

我的程序的主循环有以下(简短)伪码:

代码语言:javascript
运行
复制
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版本:

代码语言:javascript
运行
复制
    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 ) for循环,并且只需要一个专门用于此的线程。
  • omp parallel num_threads(omp_get_max_threads())用于内部for(int ) for -循环,因为我希望所有内核同时处理这部分代码。
  • omp critical位于full for(int )循环的末尾,因为我想执行线程--安全地更新当前节点。

就运行时而言,OpenMP版本通常是纯SSE版本的两倍。

在我的代码中,有什么东西是特别糟糕的吗?这会导致OpenMP的速度急剧下降吗?

EN

回答 1

Stack Overflow用户

发布于 2021-04-06 06:58:20

在我的代码中,有什么东西是特别糟糕的吗?这会导致OpenMP的速度急剧下降吗?

第一:

(Int i) for循环的顶层omp单线程,我只想要一个专用于此的线程。

在您的代码中,您有以下内容:

代码语言:javascript
运行
复制
#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),而其他线程不执行任何操作。您可以简化为:

代码语言:javascript
运行
复制
    for (int i = 1; i < totalNum; i++) 
    {
       #pragma omp parallel num_threads(omp_get_max_threads())
       {
         //....
       }

      ThisPt = minimumNode;
   }

内部仅由一个线程执行。

第二:

omp并行num_threads(omp_get_max_threads())用于内部for(int ) for -循环,因为我希望所有内核同时处理这部分代码。

问题是,这可能会返回逻辑核的数目,而不是物理核,而且一些代码在超线程处理时的性能可能会更差。因此,我首先用不同数量的线程进行测试,从2、4等开始,直到找到代码停止缩放的数字为止。

omp关键在full for(int j)循环的末尾,因为我想线程-安全地更新当前节点。

代码语言:javascript
运行
复制
        // updating the global minimumNode in a thread-safe way
        #pragma omp critical (update_minimum)
        {
            if (localMinimum < minimum) {
                minimum = localMinimum;
                minimumNode = localMinimumNode;
            }
        }

这可以通过创建一个数组来替代,其中每个线程将其局部最小值保存在保留给该线程的位置,并且在并行区域外,初始线程提取minimumminimumNode

代码语言:javascript
运行
复制
        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];
            }
        }

最后,在完成这些更改之后,您将尝试检查是否可以通过以下方法替换此并行化:

代码语言:javascript
运行
复制
    #pragma omp parallel for
    for (int i = 1; i < totalNum; i++) 
    {
       ...
    }
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66961529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档