【发布时间】:2016-12-01 12:43:49
【问题描述】:
我有这样一个信息量不足的嵌套循环(就像性能测试一样):
const int N = 300;
for (int num = 0; num < 10000; num++) {
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) {
arr[i][j] = brr[i][j];
crr[i][j] = arr[i][j] - brr[i][j];
sum1 += crr[i][j];
sum2 += arr[i][j];
}
}
}
经过的时间是
about 6 s
我尝试使用 OpenMP 并行化不同的循环。但我对我得到的结果感到非常困惑。
在第一步中,我只在第一个(最外层)循环中使用了“parallel for”编译指示:
#pragma omp parallel for schedule(static) reduction(+:sum1,sum2)
for (int num = 0; num < 10000; num++) {
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) {
arr[i][j] = brr[i][j];
crr[i][j] = arr[i][j] - brr[i][j];
sum1 += crr[i][j];
sum2 += arr[i][j];
}
}
}
经过的时间是(2个核心)
3.81
然后我尝试使用“collapse”子句(2 个核心)并行化两个内部循环:
for (int num = 0; num < 10000; num++) {
#pragma omp parallel for collapse(2) schedule(static) reduction(+:sum1, sum2)
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) {
arr[i][j] = brr[i][j];
crr[i][j] = arr[i][j] - brr[i][j];
sum1 += crr[i][j];
sum2 += arr[i][j];
}
}
}
经过的时间是
3.76
这比以前的情况要快。我不明白这是为什么。
如果我像这样使用这些内部循环的融合(这意味着在性能方面更好)
#pragma omp parallel for schedule(static) reduction(+:sum1,sum2)
for (int n = 0; n < N * N; n++) {
int i = n / N; int j = n % N;
经过的时间是
5.53
这让我很困惑。在这种情况下,性能会更差,但通常人们建议融合循环以获得更好的性能。
好的,现在让我们尝试只并行化像这样的中间循环(2 核):
for (int num = 0; num < 10000; num++) {
#pragma omp parallel for schedule(static) reduction(+:sum1,sum2)
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) {
arr[i][j] = brr[i][j];
crr[i][j] = arr[i][j] - brr[i][j];
sum1 += crr[i][j];
sum2 += arr[i][j];
}
}
}
再次,性能变得更好:
3.703
最后一步 - 仅对最内层循环进行并行化(假设根据之前的结果这将是最快的情况)(2 个核心):
for (int num = 0; num < 10000; num++) {
for (int i=0; i<N; i++) {
#pragma omp parallel for schedule(static) reduction(+:sum1,sum2)
for (int j=0; j<N; j++) {
arr[i][j] = brr[i][j];
crr[i][j] = arr[i][j] - brr[i][j];
sum1 += crr[i][j];
sum2 += arr[i][j];
}
}
}
但是(惊喜!)经过的时间是
about 11 s
这比以前的情况要慢得多。我无法理解这一切的原因。
顺便说一下,我正在寻找类似的问题,并找到了添加的建议
#pragma omp parallel
在第一个循环之前(例如,在this 和that 问题中)。但为什么它是正确的程序?如果我们把
#pragma omp parallel#
for-loop 之前意味着每个线程都完全执行了for-loop,这是不正确的(过度工作)。确实,我尝试插入
#pragma omp parallel
在最外层循环之前有不同的位置
#pragma omp parallel for
正如我在这里描述的那样,在调用情况下性能更差(此外,在仅并行化最内层循环的最新情况下,答案也是不正确的(即,“sum2”不同 - 因为存在竞争条件)。
我想知道这种性能的原因(可能是数据交换的时间大于每个线程上实际计算的时间,但这是最新的情况)以及哪种解决方案最正确一个。
编辑:我已禁用编译器的优化(通过 $-O0$ 选项),结果仍然相同(除了最新示例中经过的时间(并行化最内层循环时)从 11 减少秒到 8 秒)。 编译器选项:
g++ -std=gnu++0x -fopenmp -O0 test.cpp
变量定义:
unsigned int seed;
const int N = 300;
int main()
{
double arr[N][N];
double brr[N][N];
for (int i=0; i < N; i++) {
for (int j = 0; j < N; j++) {
arr[i][j] = i * j;
brr[i][j] = i + j;
}
}
double start = omp_get_wtime();
double crr[N][N];
double sum1 = 0;
double sum2 = 0;
【问题讨论】:
-
编写代码只是为了测试性能非常困难。
arr和crr赋值在第一次迭代后不会改变任何东西 - 所以编译器可能会优化它。如果他们这样做了,那么外部循环并行化将是不正确的。讨论这些特定的循环对于理解这些数组实际发生变化的循环没有多大帮助。另外,请附上minimal reproducible example。特别是缺少编译器选项、编译器版本和变量定义,这非常重要。 -
@Zulan 我已经禁用了编译器的优化(见编辑过的问题)并添加了变量的声明。
标签: performance for-loop parallel-processing nested openmp