【发布时间】:2021-06-28 14:35:26
【问题描述】:
我希望 OpenMP 将 大 数组缩减为较小的动态数组。例如,在哪里
large[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
// OpenMP reduce length-3 sublists of large, to produce:
small[] = {3, 6, 9};
我的要求类似于this question,但我有重要的附加限制:
- 必须支持 OpenMP 3.1(所以我不能像 this answer 那样使用 OpenMP 4.5 的数组缩减)
- 不能拥有
small的线程私有副本(如this answer),因为small可以和large一样大,因此线程私有副本可能会导致堆栈溢出 -
large数组必须按顺序迭代,以获得良好的缓存(因为它可能非常大)
其他一些细节:
-
large的求和元素不一定相邻 - 这种减少发生在函数中。
small(输出)数组由用户预先分配,并通过引用传递。理想情况下,该函数不应分配small的新本地副本 -
small和large的长度都是2的幂(例如2、4、8、16...)。small的每个元素都从相同数量的large元素减少(2 的另一个幂)。
为了清楚起见,这里是示例串行伪代码:
void myfunc(double* small, double* large, int lLen, // other args) {
for (int i=0; i<lLen; i++) {
int sInd = // determined by other args
small[sInd] += large[i];
}
}
这是一个示例实现,由于它利用了 OpenMP 4.5 的数组缩减,因此不合格。此外,它不希望地使用了small 的本地副本(根据使用reduction 的要求)。
void myfunc(double* small, int sLen, double* large, int lLen, // other args) {
double smallLocal[sLen];
int i, sInd;
#pragma omp parallel private (i) for
for (i=0; i<sLen; s++)
smallLocal[i] = 0;
#pragma omp parallel private (i, sInd) reduction (+:smallLocal) for
for (i=0; i<largeLen; i++) {
sInd = // determined by other args
smallLocal[sInd] += large[i];
}
#pragma omp parallel private (i) for
for (i=0; i<sLen; s++)
small[i] = smallLocal[i];
}
我希望我可以在 OpenMP 3.1 中实现相同的目标,甚至可以通过自己管理数组元素减少来避免 smallLocal 的本地副本。我该怎么做?
【问题讨论】:
-
不知道
SInd的确切等式会使事情变得困难。SInd可能会跳来跳去,对缓存造成严重破坏并使SIMD优化变得困难。 -
@CraigEstey 没错。
sInd由i的位子集组成;位索引作为附加参数提供。位列表不一定是连续的,也不一定是排序的。似乎不可能同时进行缓存友好读取large和缓存友好写入small。由于sLen <= lLen,优化large阅读似乎更明智 -
鉴于此,这样做可能会更快:
sInd = i >> bucket_shift或sInd = i % BUCKET_SIZE,或一些这样的缓存友好索引,然后在主循环之后根据“真实”sInd值重新排序. -
嗯,这可能吗,因为位索引可以是任何子集/顺序?无论我如何重新排序存储桶,相邻的
i值可能需要写入相距较远的存储桶(考虑位索引{0, 1, 20}) -
如果
small足够小,随机访问可能没问题,因为它将仍然保持高速缓存热[如果阵列适合您的CPU 的高速缓存大小]。根据您在下面的回答(例如 32767)将适合大多数缓存,所以您可能没问题。