C 前缀和的并行化(Openmp)

C 前缀和的并行化(Openmp),c,loops,parallel-processing,openmp,C,Loops,Parallel Processing,Openmp,我有两个向量,a[n]和b[n],其中n是一个大数 a[0] = b[0]; for (i = 1; i < size; i++) { a[i] = a[i-1] + b[i]; } a[0]=b[0]; 对于(i=1;i

我有两个向量,a[n]和b[n],其中n是一个大数

a[0] = b[0];

for (i = 1; i < size; i++) {
        a[i] = a[i-1] + b[i];
}
a[0]=b[0];
对于(i=1;i
通过这段代码,我们试图实现a[i]包含b[]中直到b[i]的所有数字的总和。我需要使用openmp并行化这个循环


主要问题是a[i]依赖于a[i-1],因此我想到的唯一直接方法是等待每个a[i-1]数字准备就绪,这需要很多时间,而且毫无意义。openmp中有解决这个问题的方法吗?

你是18世纪的卡尔·弗里德里希·高斯,你的小学老师决定用一个需要大量或普通重复运算的家庭作业问题来惩罚全班同学。在前一周,你的老师让你把前100个计数数字加起来,因为你很聪明,所以你就出来了。你的老师不喜欢这样,所以他提出了一个他认为无法优化的新问题。用你自己的符号重写这个问题,就像这样

a[0] = b[0];   
for (int i = 1; i < size; i++) a[i] = a[i-1] + b[i];
再次使用您的符号,您将问题改写为

int sum = 0;
for (int i = 0; i < size; i++) sum += b[i], a[i] = sum;
现在你知道该怎么做了。找到
t
同学。让每个人都处理数字的子集。为了简单起见,你选择的
size
是100,四个同学
t0、t1、t2、t3
然后每个同学都选择

 t0               t1                t2              t3
 s0 = sum(0,25)   s1 = sum(25,50)   s2 = sum(50,75) s3 = sum(75,100)
同时,。然后定义

fix(int n0, int n, int offset) {
    for(int i=n0; i<n; i++) a[i] += offset
}
你意识到,如果同学花大约相同的
K
秒做算术,你可以在
2*K*size/t
秒内完成这项工作,而一个人则需要
K*size
秒。很明显,你至少需要两个同学才能收支平衡。因此,有了四个同学,他们应该在一个同学的一半时间内完成

现在你用自己的符号写出你的算法

int *suma;  // array of partial results from each classmate
#pragma omp parallel
{
    int ithread = omp_get_thread_num();    //label of classmate
    int nthreads = omp_get_num_threads();  //number of classmates
    #pragma omp single
    suma = malloc(sizeof *suma * (nthreads+1)), suma[0] = 0;

    //now have each classmate calculate their partial result s = sum(n0, n)
    int s = 0;
    #pragma omp for schedule(static) nowait
    for (int i=0; i<size; i++) s += b[i], a[i] = sum;
    suma[ithread+1] = s;

    //now wait for each classmate to finish
    #pragma omp barrier

    // now each classmate sums each of the previous classmates results
    int offset = 0;
    for(int i=0; i<(ithread+1); i++) offset += suma[i];

    //now each classmates corrects their result 
    #pragma omp for schedule(static)
    for (int i=0; i<size; i++) a[i] += offset;
}
free(suma)
int*suma;//每个同学的部分结果数组
#pragma-omp并行
{
int ithread=omp_get_thread_num();//同学标签
int nthreads=omp_get_num_threads();//同学人数
#布拉格omp单曲
suma=malloc(sizeof*suma*(nthreads+1)),suma[0]=0;
//现在让每个同学计算他们的部分结果s=sum(n0,n)
int s=0;
#计划(静态)nowait的pragma omp

对于(int i=0;iI)这个家庭作业是偶然的?为什么你需要使它并行?对我来说似乎是一个顺序问题。如果你对各种向量这样做,它可以并行,而不仅仅是
a
。主要的问题是a[i]依赖于a[i-1]哦,不,实际上不是。
a[i]
和(b[0]…b[i]
)。您已经草拟了一个串行方式来计算它,但串行性不是计算的基本功能。请将您最喜欢的搜索引擎指向并行前缀sum。@villintehaspam不是,但我看不出这会如何改变这里的问题。@Alex我需要将其并行化,以减少计算时间,因为n是一个真正的large编号。在同一时间读取和写入不同的数字子集不会仍然是读/写带宽限制吗?是否有一个技术名称,以便我可以进一步研究如何做到这一点?我发现该程序的通用版本几乎是并行版本的两倍。这是因为被读取/写入带宽限制吗rite带宽限制还是因为对整个大小的向量进行了两次迭代?谢谢你的回答,这非常有用。@paraleljoe,几年前我提出这个解决方案时,我不理解内存带宽限制的含义。这是我认为你应该理解的关于并行计算的最重要概念。如果
size
如果比最后一级缓存大,那么我的解决方案可能会更糟,所以这可能就是它速度较慢的原因。您可以改进它,分块执行并行部分,并将每个块的总和传递给下一个chuck。但最好的办法是以与顺序版本一样快的速度获得混合并行版本。@ParallelJoe,我的解决方案uld可能适用于多插槽系统/NUMA系统上的大型
size
。因此,每个插槽但不是每个核心。您也可以在分布式计算中使用它。到目前为止,对于单个插槽系统,它只适用于
size
不太大的情况。@paraleljoe,我在我的答案中添加了一个关于内存带宽限制的链接。我建议您阅读它。顺便说一句,我有很多关于你可以阅读的问题的答案。我并不声称自己是这方面的专家。就像我说的,在我理解内存带宽限制的真正含义之前,我已经想出了这个解决方案。@paraleljoe,你应该在你的问题中添加
size
的大小,以及为什么你需要为这么大的大小添加前缀和。我不知道为什么您需要对非常大的
大小
进行前缀和。可能您可以将前缀和与其他计算一起分块进行,以尝试克服内存带宽。如果您需要更多帮助,您需要添加此信息。
 t0               t1                t2              t3
 s0 = sum(0,25)   s1 = sum(25,50)   s2 = sum(50,75) s3 = sum(75,100)
fix(int n0, int n, int offset) {
    for(int i=n0; i<n; i++) a[i] += offset
}
t0             t1               t2                  t3 
fix(0, 25, 0)  fix(25, 50, s0)  fix(50, 75, s0+s1)  fix(75, 100, s0+s1+s2)
int *suma;  // array of partial results from each classmate
#pragma omp parallel
{
    int ithread = omp_get_thread_num();    //label of classmate
    int nthreads = omp_get_num_threads();  //number of classmates
    #pragma omp single
    suma = malloc(sizeof *suma * (nthreads+1)), suma[0] = 0;

    //now have each classmate calculate their partial result s = sum(n0, n)
    int s = 0;
    #pragma omp for schedule(static) nowait
    for (int i=0; i<size; i++) s += b[i], a[i] = sum;
    suma[ithread+1] = s;

    //now wait for each classmate to finish
    #pragma omp barrier

    // now each classmate sums each of the previous classmates results
    int offset = 0;
    for(int i=0; i<(ithread+1); i++) offset += suma[i];

    //now each classmates corrects their result 
    #pragma omp for schedule(static)
    for (int i=0; i<size; i++) a[i] += offset;
}
free(suma)