c++ - 如何在每个例程OpenMP中处理子数组

标签 c++ arrays parallel-processing openmp

我有一些代码,并行计算一些数组前缀的总和(例如 out_arr[0] = in_arr[0], out_arr[1 ] = in_arr[0]+in_arr[1] .. 等)。 我的代码有 N 个线程,N 是一些 in_arr 元素,每个线程只处理数组的 1 个元素。这不是好的解决方案,所以我想在每个线程中处理 N/num_of_threads,但我失败了。

我尝试用 N/num_of_threads 值创建共享变量,并在第一个 #pragma 指令后面用这个变量组织 for 循环,但我无法在标准输出中调试那些神奇的数字。

这是“坏”解决方案的工作版本:

void CalcSum2(int a[], int s[], int n) { 
    int* old = new int [n], *cnt = new int [n]; 
    #pragma omp parallel num_threads(N) {
    int i = omp_get_thread_num(), d = 1; 
    s[i] = a[i]; 
    cnt[i] = 1; 
     #pragma omp barrier 
    while (d < n) { 
        old[i] = s[i]; 
     #pragma omp barrier 
         if (i >= d) { 
             s[i] += old[i-d]; 
         cnt[i]++; 
         } 
         d += d; 
     #pragma omp barrier 
    }
    }
    delete[] old; delete[] cnt; 
    return; 
} 

最佳答案

您正在计算累加和。也称为前缀和。这可以与 OpenMP 并行完成。我最近用 OpenMP 解决了这个问题 Parallel cumulative (prefix) sums in OpenMP: communicating values between threads

您必须并行遍历数组两次。第一次做部分和,第二次用偏移量校正部分和。

我在下面为您将您的代码转换成了这个。作为测试,我计算了计数的总和,它具有 i*(i+1)/2 的封闭形式解。您可以看到 prefix_sum 函数得到了正确的答案。

#include <stdio.h>
#include <omp.h>

void prefix_sum(int a[], int s[], int n) {
    int *suma;
    #pragma omp parallel
    {
        const int ithread = omp_get_thread_num();
        const int nthreads = omp_get_num_threads();
        #pragma omp single
        {
            suma = new int[nthreads+1];
            suma[0] = 0;
        }
        int sum = 0;
        #pragma omp for schedule(static) nowait // do partial sum in parallel
        for(int i=0; i<n; i++) {
            sum += a[i];
            s[i] = sum;
        }
        suma[ithread+1] = sum;
        #pragma omp barrier
        int offset = 0;
        for(int i=0; i<(ithread+1); i++) {
            offset += suma[i];
        }

        #pragma omp for schedule(static) //run over array again in parallel for full sum
        for(int i=0; i<n; i++) {
            s[i] += offset;
        }
    }
    delete[] suma;
}

int main() {
    const int n = 100;
    int *a = new int[n];
    int *s = new int[n];
    for(int i=0; i<n; i++) {
        a[i] = i;
    }
    prefix_sum(a, s, n);
    for(int i=0; i<n; i++) {
        printf("%d ", s[i]);
    } printf("\n");

    for(int i=0; i<n; i++) {
        printf("%d ", i*(i+1)/2);
    } printf("\n");
}

编辑 此方法的问题之一是,对于大型数组,大多数值在第二次传递开始时已从缓存中逐出。我想出了一个解决方案,它并行运行一个 block ,然后按顺序移动到下一个 block 。我将 chunck_size 设置为二级缓存(由于有四个内核,实际上是四倍)。这为更大的阵列提供了很大的改进。这是该功能的概述。完整的功能可以在我的回答中找到 simd-prefix-sum-on-intel-cpu .

void scan_omp_SSEp2_SSEp1_chunk(float a[], float s[], int n) {
    float *suma;
    const int chunk_size = 1<<18;
    const int nchunks = n%chunk_size == 0 ? n / chunk_size : n / chunk_size + 1;    
    #pragma omp parallel
    {
        //initialization code 
        for (int c = 0; c < nchunks; c++) {
            const int start = c*chunk_size;
            const int chunk = (c + 1)*chunk_size < n ? chunk_size : n - c*chunk_size; 
            //pass1: pass1_SSE(&a[start], &s[start], chunk);                
            //get offset
            //pass2: pass2_SSE(&s[start], offset, chunk);
        }
    }
    delete[] suma;
}

关于c++ - 如何在每个例程OpenMP中处理子数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19428128/

相关文章:

c++ - 一次播放多个声音并从我们离开的地方继续

c++ - getaddrinfo() 只返回::1 作为 IPV6 地址,

c - 将每个变量打印到文件中

algorithm - Clojure 中的并发笛卡尔积算法

c++ - 如何在没有 std::initializer_list 的情况下列出初始化模板类,使其具有固定大小

c++ - 在 Qt 中注册元类型的模式

linux - bash 中的平衡指数

c++ - 如何将txt文件中的多个值输入数组C++

java - 我的对象列表应该有多大才能保证使用 java 8 的 parallelStream?

r - 如何在并行计算期间写出日志?如何调试并行计算?