你正在做一个累积和。也称为前缀和。这可以与 OpenMP 并行完成。我最近使用 OpenMP 中的 OpenMP并行累积(前缀)总和解决了这个问题:在线程之间通信值
您必须在阵列上并行运行两次。第一次进行部分和,第二次用偏移量校正部分和。
我在下面为您转换了您的代码。作为测试,我做了计数的总和,它有一个封闭形式的解决方案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");
}
编辑
此方法的一个问题是,对于大型数组,大多数值在第二遍开始时已从缓存中逐出。我想出了一个解决方案,它并行运行一个块,然后依次移动到下一个块。我将 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;
}