for (uint i = 0; i < x; i++) {
for (uint j = 0; j < z; j++) {
if (inFunc(p, index)) {
XY[2*nind] = i;
XY[2*nind + 1] = j;
nind++;
}
}
}
此处x = 512且z = 512且nind = 0 和XY [2 * x * y]。
我想用openMP优化这个循环,但'nind'变量紧密绑定到for循环。我没有任何线索,因为我也在检查一个条件,所以有些时候它不会进入,如果并且将跳过增量或者它将进入增量nind。 openMP线程将增加nind变量,因为先来将首先增加nind。有没有办法解开它。 ('绑定'我的意思是只能连续实现)。
答案 0 :(得分:2)
在这种情况下,典型的缓存友好解决方案是在私有数组中收集(i,j)
对,然后在最后连接这些私有数组,最后根据需要对结果进行排序:
#pragma omp parallel
{
uint myXY[2*z*x];
uint mynind = 0;
#pragma omp for collapse(2) schedule(dynamic,N)
for (uint i = 0; i < x; i++) {
for (uint j = 0; j < z; j++) {
if (inFunc(p, index)) {
myXY[2*mynind] = i;
myXY[2*mynind + 1] = j;
mynind++;
}
}
}
#pragma omp critical(concat_arrays)
{
memcpy(&XY[2*nind], myXY, 2*mynind*sizeof(uint));
nind += mynind;
}
}
// Sort the pairs if needed
qsort(XY, nind, 2*sizeof(uint), compar);
int compar(const uint *p1, const uint *p2)
{
if (p1[0] < p2[0])
return -1;
else if (p1[0] > p2[0])
return 1;
else
{
if (p1[1] < p2[1])
return -1;
else if (p1[1] > p2[1])
return 1;
}
return 0;
}
您应该在N
子句中尝试schedule(dynamic,N)
的不同值,以便在开销(对于N
的小值)和负载不平衡之间实现最佳权衡(对于大值N
)。比较函数compar
可能以更优化的方式编写。
这里的假设是合并和排序数组的开销很小。是否会出现这种情况取决于很多因素。
答案 1 :(得分:1)
以下是Hristo Iliev的一个很好的答案。
在这里采取行动的重要参数是成对的索引而不是成对本身。
我们可以为每个线程并行填充对索引的私有数组。将对每个线程的数组进行排序(与调度无关)。
以下函数合并两个已排序的数组
void merge(int *a, int *b, int*c, int na, int nb) {
int i=0, j=0, k=0;
while(i<na && j<nb) c[k++] = a[i] < b[j] ? a[i++] : b[j++];
while(i<na) c[k++] = a[i++];
while(j<nb) c[k++] = b[j++];
}
这是剩下的代码
uint nind = 0;
uint *P;
#pragma omp parallel
{
uint myP[x*z];
uint mynind = 0;
#pragma omp for schedule(dynamic) nowait
for(uint k = 0 ; k < x*z; k++) {
if (inFunc(p, index)) myP[mynind++] = k;
}
#pragma omp critical
{
uint *t = (uint*)malloc(sizeof *P * (nind+mynind));
merge(P, myP, t, nind, mynind);
free(P);
P = t;
nind += mynind;
}
}
然后在k
中给出一个索引P
,该对为(k/z, k%z)
。
可以改善合并。现在它在O(omp_get_num_threads())
,但它可以在O(log2(omp_get_num_threads()))
中完成。我没有理会这件事。
Hristo Iliev指出,动态调度并不能保证每个线程的迭代单调增加。我认为在实践中它们是原则上但不保证。
如果你想100%确定迭代单调增加,你可以implement dynamic scheduling by hand。
答案 2 :(得分:0)
您提供的代码看起来就像是在尝试以顺序顺序填充XY数据。在这种情况下,OMP多线程可能不是工作的工具,因为线程(在最好的情况下)应尽可能避免通信。你可以引入一个原子计数器,但是再一次,它可能会更快,只是按顺序执行。
您还想通过优化实现什么目标? x和z不是太大,所以我怀疑即使你以平行的方式重新解决你的问题,你的速度也会大幅增加。
如果你想要并行执行 - 将索引映射到数组,例如(未经测试,但应该做)
#pragma omp parallel for shared(XY)
for (uint i = 0; i < x; i++) {
for (uint j = 0; j < z; j++) {
if (inFunc(p, index)) {
uint idx = (2 * i) * x + 2 * j;
XY[idx] = i;
XY[idx + 1] = j;
}
}
}
但是,您的数组XY中会有间隙。这对你来说可能是一个问题,也可能不是。