C 一种高效的阵列滤波并行算法
给定一个非常大的数组,我只想选择符合某些条件的元素。我先验地知道要匹配的元素的数量。我当前的伪代码是:C 一种高效的阵列滤波并行算法,c,multithreading,concurrency,parallel-processing,openmp,C,Multithreading,Concurrency,Parallel Processing,Openmp,给定一个非常大的数组,我只想选择符合某些条件的元素。我先验地知道要匹配的元素的数量。我当前的伪代码是: filter(list): out = list of predetermined size i = 0 for element in list if element matches condition out[i++] = element return out 在尝试并行化之前的算法时,我的天真方法是将i原子的增量作为OpenMP项目的一部分,因此使用p
filter(list):
out = list of predetermined size
i = 0
for element in list
if element matches condition
out[i++] = element
return out
在尝试并行化之前的算法时,我的天真方法是将i原子的增量作为OpenMP项目的一部分,因此使用pragma omp原子。但是,即使与串行实现相比,这种实现也会降低性能。有什么更有效的算法来实现这一点?为什么我会有如此严重的减速
评论中的信息:
我在OpenMP中使用C;
目前正在测试一百万个条目;
串行大约需要7秒,两个线程并行大约需要15秒;
输出数组的大小正好是问题的一半,等于找到数组的元素小于所述数组的中值;
我只测试了两个内核。
但是,与其他实现相比,此实现降低了性能
甚至是串行实现。还有什么更有效的算法
有什么办法来实现这一点
瓶颈是原子操作的开销,因此乍一看,更有效的算法应该是避免使用这种操作的算法。虽然这是可能的,但代码需要两步方法,例如,每个线程将在私有数组中保存找到的元素。在并行区域之后,主线程将收集每个线程找到的所有元素,并将它们合并到单个数组中
合并到单个阵列的第二部分也可以并行或使用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;
}
因此,并行版本要慢得多,这是可以预期的,因为并行执行的工作根本不足以克服原子和并行的开销
我还测试了移除原子并离开竞赛条件,只是为了检查时间:
-> Sequential : 0.001597 (s)
-> 2 Threads : 0.001283 (s)
-> 4 Threads : 0.000720 (s)
因此,在没有原子线程的情况下,2个和4个线程的加速比分别约为1.2和2.2。因此,自然原子会造成巨大的开销。尽管如此,即使没有任何原子能,加速效果也不是很好。这是您可以从单独并行化代码中得到的最多的结果
根据您的实际代码,以及您的条件对计算的要求,即使使用我提到的第二种方法,您也可能无法获得很好的加速
以下评论中的有用注释:
即使它不会加速这个特定的例子,它也可能会加速
指出这样的问题通常是在
使用并行排他扫描算法/前缀的并行计算
求和以确定哪个线程从输出的哪个索引开始
大堆然后每个线程都有一个私有的输出索引,它正在递增
一个人不需要原子
在这种情况下,这种方法可能比@dreamcrashs解决方案慢
这是一种特殊情况,但更一般的情况是,每种情况都有专用数组
螺纹在确定其尺寸等方面可能非常棘手,尤其是在
您的输出大于每个输入元素所在的输入情况
不提供0或1个输出元素,但提供n个输出元素
-> Sequential : 0.001597 (s)
-> 2 Threads : 0.001283 (s)
-> 4 Threads : 0.000720 (s)