假设我有一个N个元素的向量(数组,列表,等等......)V,假设V 0 到V (N-1)。对于每个元素V i ,需要为每个索引j计算函数f(V i ,V j )(包括情况i) = j)的。该函数是对称的,因此一旦计算出f(V i ,V j ),就不需要重新计算f(V j , V <子> I 子>)。然后我们对函数进行N(N + 1)/ 2次总评估,使其成为O(N 2 )算法。让我们假设计算f所花费的时间相对较长但是一致。
现在,我想并行执行算法。我需要确定(某些M个)工作线程的调度,以便两个线程不会同时使用相同的内存部分(即相同的元素)。例如,f(V 1 ,V 2 )可以与f并行求值(V 3 ,V 4 ),但不与f平行(V 2 ,V 3 )。工作流程分为几个步骤,每个步骤,每个工作线程执行一次f的评估。然后线程被同步,之后它们继续进行下一步,依此类推。
问题是,我如何确定(最好是最佳)每个线程的时间表作为一系列索引对(i,j),以便解决完整的问题(即每个索引对只访问一次,考虑到对称性)?虽然直接的答案当然是好的,但我也很欣赏指向算法甚至相关网站/文献的指针。
答案 0 :(得分:5)
这让我想起了一个常见的体育赛程安排问题:在N队的联赛中,安排N-1个比赛日,这样每个队伍每天都有一场比赛,每场比赛一次。
下棋,这个问题有一个非常具有说明性的解决方案。将所有电路板并排排列在长桌上。一名球员总是在同一把椅子上。其他玩家在跳过该玩家的同一方向围绕桌子旋转。答案 1 :(得分:0)
让我们看看直接实施:
for(i=0; i < N; ++i) { for(j=1; j < i; ++J) { compute i,j pair and assign to i,j, and j,i result } }
我是C ++程序员,所以我可以考虑使用OpenMP进行外部循环并行化:
#pragma OMP parallel for for(i=0; i < N; ++i) { .... }
如果你不知道OpenMP,它只是根据处理器的数量将循环划分为n个循环,并且并行执行它们。我不认为在这种情况下结果会很好,因为每个i + 1循环都比i循环短。让我们通过这种方式编写这个算法,所有循环都具有相同的长度。循环总数将为N / 2。第一个循环处理0和N-1行。第二个循环处理1和N-2行等。这样的循环可以成功并行化,没有冲突。简化代码,没有关于偶数/奇数N等的详细信息:
#pragma OMP parallel for for(i=0; i < N/2; ++i) { Handle i value ... Handle N - 1 - i value ... }
这只是一般性的想法,你可以解决不正确的细节。
答案 2 :(得分:0)
我看到两种可能性。 一种是在先前的M个线程中划分K = N(N + 1)/ 2个任务。以下只是伪代码:
allocateTasks() {
num = ceil(N*(N+1)/2/M); // number of tasks per thread
i0 = 0, j0 = 0;
n = 0;
for (i = 0; i < N; ++i) {
for (j = i; j < N; ++j) { // skip symmetries
if (n == 0) {i0 = i; j0 = j;}
++n;
if (n == num) {
thread(i0, j0, num);
n = 0;
}
}
}
if (n > 0) thread(i0, j0, n);
}
thread(i0, j0, num) {
// perform num tasks starting at (i0, j0)
i = i0; j = j0;
for (n = 0; n < num; ++n) {
// f[i, j] = ...
++j;
if (j >= N) {++i; j = i;}
}
}
这里的问题是当f(i,j)的计算不同时, 那么线程之间的负载就不平衡了。
所以,也许第二种方法更好。每个线程都使用下一个线程 任务。没有全局预分配任务。
// init boolean array: done[0..N-1, 0..N-1] = false
thread() {
for (i = 0; i < N; ++i) {
for (j = i; j < N; ++j) { // skip symmetries
boolean mytask = false;
lock(mutex); // synchronize access to global array done[][]
if (!done[i, j]) {
done[i, j] = true;
mytask = true;
}
unlock(mutex);
if (mytask) {
f(i, j) = ...
}
}
}
}
在任何情况下,访问f(i,j)都将通过
g(i, j) = j >= i ? f(i, j) : f(j, i)