【问题标题】:Parallelizing for-loop and merging the thread private variables并行化for循环并合并线程私有变量
【发布时间】:2021-04-09 15:37:12
【问题描述】:

我对如何使用 OpenMP 多线程来并行化我正在使用的这个 for 循环有点困惑。在程序的这个程序部分中,我尝试从数组 x 和 y 接收数据;定义为:

x = (float*)aligned_alloc(32, sizeof(float) * n);
y = (float*)aligned_alloc(32, sizeof(float) * n);

其中 n 是大于 0 且可除以 16 的 int,然后将最大/最小 x/y 值保存在向量 maxX、minX、minY 和 maxY 中。 n 可以尽可能大,但在测试时我的 n 为 360000000。

我试图与多线程并行化的 for 循环是

for(int i = 8; i < n; i+= 8 ){
    __m256 vx = _mm256_load_ps(&x[i]);
    __m256 vy = _mm256_load_ps(&y[i]);

    minX = _mm256_min_ps(minX, vx);
    maxX = _mm256_max_ps(maxX, vx);

    minY = _mm256_min_ps(minY, vy);
    maxY = _mm256_max_ps(maxY, vy);

}

其中 minX、maxX、minY 和 maxY 是用零填充的 _m256 个向量。

到目前为止,我的尝试使我让每个线程都有自己的私有临时变量,用于 minX、maxX、minY 和 maxY,在 for 循环中处理,然后尝试将私有变量合并到共享变量中将在程序的其余部分中使用,如下所示:

    #pragma omp parallel num_threads(4)
    {
        __m256 TempMinX = _mm256_load_ps(&x[0]); //creaing private variables for each thread
        __m256 TempMaxX = _mm256_load_ps(&x[0]);
        __m256 TempMinY = _mm256_load_ps(&x[0]);
        __m256 TempMaxY = _mm256_load_ps(&x[0]);

        #pragma omp for
        for (int i = 8; i < n; i += 8) {
                            
            __m256 vx = _mm256_load_ps(&x[i]); //loads the values from the x array
            __m256 vy = _mm256_load_ps(&y[i]); //loads the values from the y array

                TempMinX = _mm256_min_ps(TempMinX, vx); 
                TempMaxX = _mm256_max_ps(TempMaxX, vx);
                TempMinY = _mm256_min_ps(TempMinY, vy);
                TempMaxY = _mm256_max_ps(TempMaxY, vy);

            }
            /*section to merge thread private variables into
              the shared variables by comparing the 
              values in vector minX with the threads private 
              vector Temp and saving the smalles/largest         
              values in the shared vector: */

                #pragma omp critical
                minX = _mm256_min_ps(minX, TempMinX);
                #pragma omp critical
                maxX = _mm256_max_ps(maxX, TempMaxX);
                #pragma omp critical
                minY = _mm256_min_ps(minY, TempMinY);
                #pragma omp critical
                maxY = _mm256_max_ps(maxY, TempMaxY);

当运行这个程序并将其与“未并行化”程序进行比较时,“未并行化”程序比我的“并行化”程序运行得更快。我怀疑这可能与“合并”部分有关,其中不同的线程必须等待其他线程访问共享变量才能写入它,但到目前为止我还没有找到/想出任何好的解决方案关于如何解决这个问题并让它运行得更快...

【问题讨论】:

    标签: c++ multithreading performance parallel-processing openmp


    【解决方案1】:

    问题很可能是关键区域的开销。

            #pragma omp critical
            minX = _mm256_min_ps(minX, TempMinX);
            #pragma omp critical
            maxX = _mm256_max_ps(maxX, TempMaxX);
            #pragma omp critical
            minY = _mm256_min_ps(minY, TempMinY);
            #pragma omp critical
            maxY = _mm256_max_ps(maxY, TempMaxY);
    

    哪一个可以减少整个块的一个关键区域:

            #pragma omp critical
            {
                minX = _mm256_min_ps(minX, TempMinX);
                maxX = _mm256_max_ps(maxX, TempMaxX);
                minY = _mm256_min_ps(minY, TempMinY);
                maxY = _mm256_max_ps(maxY, TempMaxY);
            }
    

    或者通过named critical regions,即:

            #pragma omp critical(region1)
            minX = _mm256_min_ps(minX, TempMinX);
            #pragma omp critical(region2)
            maxX = _mm256_max_ps(maxX, TempMaxX);
            #pragma omp critical(region3)
            minY = _mm256_min_ps(minY, TempMinY);
            #pragma omp critical(region4)
            maxY = _mm256_max_ps(maxY, TempMaxY);
    

    通过这种方式,可以让多个线程同时执行不同的命名关键区域。

    尝试两个版本,看看哪个版本的开销最小。

    另一种方法,可能是性能更好的方法,您可以将这些私有变量添加到一个数组中,该数组将由并行区域外的主线程获取:

    // create a arrays of size equal to the number of threads
    #pragma omp parallel num_threads(4)
    {
        #pragma omp for
        for (int i = 8; i < n; i += 8) {
             ...
        }
        int threadID = omp_get_thread_num();
        array_minX[threadID] = TempMinX;
        array_maxX[threadID] = TempMaxX;
        array_minY[threadID] = TempMinY;
        array_maxY[threadID] = TempMaxY;
     }
     // the master thread calculate the _mm256_min_ps of the array array_minX, and so on.
     
    

    最后,您可以使用 OpenMP 4.0 User-defined Reductions 创建自己的缩减,这基本上是上述方法所做的,但不使用 OpenMP 内置功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-20
      • 2017-09-25
      • 2019-03-09
      • 1970-01-01
      相关资源
      最近更新 更多