【发布时间】:2021-02-09 02:01:40
【问题描述】:
我有两个带整数参数的函数;称它们为 f 和 g。我还有另一个函数 h 采用两个整数参数。给定一个大小为 D 的正方形 U(意思是:{m0,m0+1,..,m0+D-1}x{n0,n0+1,...,n0+D-1}),我有一个程序用于计算 f(n) g(m) h(n,m) 在时间上在 D 上大致线性的总和,给定数组 farr, garr 包含 f(m0),f(m0+1),.. .,f(m0+D-1) 和 g(n0),g(n0+1),...,g(n0+D-1);让我们将该过程视为一个黑匣子,例如,我们通过调用 Sum(farr,garr,m0,n0,D) 来调用它。我们可以计算 farr[0]=f(m0),...farr[D-1]=f(m0+D-1) 或 garr[0]=garr(n0),garr[1]=g(n0 +1),...,garr[n0+D-1] 通过调用 Fillf(f,m0,D) 和 Fillg(g,m0,D) 在时间上大致呈线性关系。
问题是如何有效计算 {0,1,...,rD-1}x{ 中所有 (n,m) 的 f(n) g(m) h(n,m) 和0,1,...,rD-1}(比如说)并行。这在抽象上很容易——我想知道的是如何在 OpenMP 中做到这一点。
最简单的方法可能是这样的:
S=0;
#pragma omp parallel for collapse(2) schedule(dynamic) private(m0,n0,farr,garr) reduction(+:S)
for(m0=0; m0<r*D; m0+=D)
for(n0=0; n0<r*D; n0+=D)
farr = (short *) calloc(D,sizeof(int));
Fillf(farr,m0,D);
garr = (short *) calloc(D,sizeof(int));
Fillg(garr,n0,D);
S+=Sum(farr,garr,m0,n0,D)
free(garr);
free(farr);
这很好用,但它的缺点是farr 和garr 的每个段都被计算r 次而不是一次。算不上悲剧,因为整体计算复杂度没有改变(不会比 O(r^2 D) 好),但还是不可取的。
另一种方法是写
S=0;
#pragma omp parallel for schedule(dynamic) private(m0,n0,farr,garr) reduction(+:S)
for(m0=0; m0<r*D; m0+=D) {
farr = (short *) calloc(D,sizeof(int));
Fillf(farr,m0,D);
for(n0=0; n0<r*D; n0+=D) {
garr = (short *) calloc(D,sizeof(int));
Fillg(garr,n0,D);
S+=Sum(farr,garr,m0,n0,D)
free(garr);
}
free(farr);
}
这也是一个可行的解决方案,但是:(a) garr 的每个段仍然被计算 r 次而不是一次,(b) 如果可用线程的数量远大于 r,则并行化将效率低下(但小于r^2)。这里我们不能使用collapse(2),因为两个循环之间有一些事情发生。
显然应该可以做得更好。使用 OpenMP 对或多或少明显的过程进行编码的直接方法是什么? (应该预先计算大小约为 sqrt(s) D 的 farr 和 garr 的段,其中 s 是可用线程的数量,然后使用 collapse(2) 进行嵌套循环以获取 m0 和 n0在大约 D sqrt(s) 的长度段上?)
【问题讨论】:
标签: c++ c openmp nested-loops