但是,与
甚至串行实现。更有效的算法是什么
有没有实现这个?
瓶颈是原子操作的开销,因此乍一看更有效的算法将是避免使用这种操作的算法。尽管这是可能的,但代码将需要两步方法,例如每个线程会将其找到的元素保存在私有数组中。在并行区域之后,main 线程将收集每个线程找到的所有元素并将它们合并到一个数组中。
合并到单个数组的第二部分也可以并行或使用 SIMD 指令。
我创建了一个小代码来模仿你的伪代码:
#include <time.h>
#include <omp.h>
int main ()
{
int array_size = 1000000;
int *a = malloc(sizeof(int) * array_size);
int *out = malloc(sizeof(int) * array_size/2);
for(int i = 0; i < array_size; i++)
a[i] = i;
double start = omp_get_wtime();
int i = 0;
#pragma omp parallel for
for (int n=0 ; n < array_size; ++n ){
if(a[n] % 2 == 0){
int tmp;
#pragma omp atomic capture
tmp = i++;
out[tmp] = a[n];
}
}
double end = omp_get_wtime();
printf("%f\n",end-start);
free(a);
free(out);
return 0;
}
我做了一个 ad hoc 基准测试,结果如下:
-> Sequential : 0.001597 (s)
-> 2 Threads : 0.017891 (s)
-> 4 Threads : 0.015254 (s)
因此并行版本要慢得多,这是可以预料的,因为并行执行的工作根本不足以克服 原子 和并行性的开销。
我也测试过删除 atomic 并留下 race-condition 只是为了检查时间:
-> Sequential : 0.001597 (s)
-> 2 Threads : 0.001283 (s)
-> 4 Threads : 0.000720 (s)
因此,如果没有原子,2 和 4 线程的加速分别约为 1.2 和 2.2。所以自然地 atomic 会造成巨大的开销。尽管如此,即使没有任何原子,加速也不是很好。这是您对单独并行代码的最大期望。
根据您的实际代码以及您的条件对计算的要求如何,即使使用我提到的第二种方法,您也可能无法获得很大的加速。
来自@Paul G的 cmets 的有用说明:
即使它不会加快这个特定示例的速度,它也可能
说明这样的问题通常在
使用(并行)独占扫描算法/前缀的并行计算
sum 确定哪个线程从输出的哪个索引开始
大批。然后每个线程都有一个私有输出索引,它正在递增
而且不需要原子。
这种方法可能比@dreamcrashs 解决方案慢
特殊情况,但更普遍的是每个都有私有数组
线程可能非常棘手(确定它们的大小等),特别是如果
您的输出大于输入(每个输入元素的情况
不给出 0 或 1 个输出元素,但给出 n 个输出元素)。