将 OpenMP 应用于 C++ 中的特定嵌套循环
Applying OpenMP to particular nested loops in C++
我在使用 openmp 并行化一段代码时遇到问题,我认为某些必须按顺序进行的操作在概念上存在问题。
else if (PERF_ROWS <= MAX_ROWS && function_switch == true)
{
int array_dist_perf[PERF_ROWS];
int array_dist[MAX_ROWS];
#pragma omp parallel for collapse(2)
for (int i = 0; i < MAX_COLUMNS;
i = i + 1 + (i % PERF_CLMN == 0 ? 1:0))
{
for (int j = 0; j < PERF_ROWS; j++) //truncation perforation
{
array_dist_perf[j] = abs(input[j] - input_matrix[j][i]);
}
float av = mean(PERF_ROWS, array_dist_perf);
float score = score_func(av);
if (score > THRESHOLD_SCORE)
{
for (int k = 0; k < MAX_ROWS; k++)
{
array_dist[k] = abs(input[k] - input_matrix[k][i]);
}
float av_real = mean(MAX_ROWS, array_dist);
float score_real = score_func(av_real);
rank_function(score_real, i);
}
}
}
错误是"collapsed loops are not perfectly nested"。我在 g++-5 上使用 Clion。提前致谢
首先,完美嵌套的循环有以下形式:
for (init1; cond1; inc1)
{
for (init2; cond2; inc2)
{
...
}
}
请注意,外循环的主体仅由内循环组成,没有其他任何内容。这绝对不是你的代码的情况——你在内部循环之后还有很多其他语句。
其次,您的外循环不是 OpenMP 要求的规范形式。规范是可以轻松预先确定迭代次数和迭代步骤的循环。由于您正在做的是每次 i
是 PERF_CLMN
的倍数时跳过迭代,您可以将循环重写为:
for (int i = 0; i < MAX_COLUMNS; i++)
{
if (i % PERF_CLMN == 1) continue;
...
}
这将导致工作不平衡,具体取决于 MAX_COLUMNS
是否是线程数的倍数。但是还有另一个来源或不平衡,即 rank_function()
的条件评估。因此,您应该利用动态调度。
现在,显然两个 array_dist*
循环都是私有的,它们不是您的情况,这将导致数据竞争。要么在循环体内移动数组的定义,要么使用 private()
子句。
#pragma omp parallel for schedule(dynamic) private(array_dist_perf,array_dist)
for (int i = 0; i < MAX_COLUMNS; i++)
{
if (i % PERF_CLMN == 1) continue;
...
}
现在,对于一些未经请求的优化建议:两个内部循环是多余的,因为第一个基本上是第二个工作的子集。您可以通过仅使用单个数组来优化计算并节省内存,并让第二个循环从第一个循环结束的地方继续。代码的最终版本应如下所示:
else if (PERF_ROWS <= MAX_ROWS && function_switch == true)
{
int array_dist[MAX_ROWS];
#pragma omp parallel for schedule(dynamic) private(array_dist)
for (int i = 0; i < MAX_COLUMNS; i++)
{
if (i % PERF_CLMN == 1) continue;
for (int j = 0; j < PERF_ROWS; j++) //truncation perforation
{
array_dist[j] = abs(input[j] - input_matrix[j][i]);
}
float av = mean(PERF_ROWS, array_dist);
float score = score_func(av);
if (score > THRESHOLD_SCORE)
{
for (int k = PERF_ROWS; k < MAX_ROWS; k++)
{
array_dist[k] = abs(input[k] - input_matrix[k][i]);
}
float av_real = mean(MAX_ROWS, array_dist);
float score_real = score_func(av_real);
rank_function(score_real, i);
}
}
}
另一个优化潜力在于 input_matrix
不是以缓存友好的方式访问的。转置它会导致列数据在内存中连续存储并改善内存访问局部性。
我在使用 openmp 并行化一段代码时遇到问题,我认为某些必须按顺序进行的操作在概念上存在问题。
else if (PERF_ROWS <= MAX_ROWS && function_switch == true)
{
int array_dist_perf[PERF_ROWS];
int array_dist[MAX_ROWS];
#pragma omp parallel for collapse(2)
for (int i = 0; i < MAX_COLUMNS;
i = i + 1 + (i % PERF_CLMN == 0 ? 1:0))
{
for (int j = 0; j < PERF_ROWS; j++) //truncation perforation
{
array_dist_perf[j] = abs(input[j] - input_matrix[j][i]);
}
float av = mean(PERF_ROWS, array_dist_perf);
float score = score_func(av);
if (score > THRESHOLD_SCORE)
{
for (int k = 0; k < MAX_ROWS; k++)
{
array_dist[k] = abs(input[k] - input_matrix[k][i]);
}
float av_real = mean(MAX_ROWS, array_dist);
float score_real = score_func(av_real);
rank_function(score_real, i);
}
}
}
错误是"collapsed loops are not perfectly nested"。我在 g++-5 上使用 Clion。提前致谢
首先,完美嵌套的循环有以下形式:
for (init1; cond1; inc1)
{
for (init2; cond2; inc2)
{
...
}
}
请注意,外循环的主体仅由内循环组成,没有其他任何内容。这绝对不是你的代码的情况——你在内部循环之后还有很多其他语句。
其次,您的外循环不是 OpenMP 要求的规范形式。规范是可以轻松预先确定迭代次数和迭代步骤的循环。由于您正在做的是每次 i
是 PERF_CLMN
的倍数时跳过迭代,您可以将循环重写为:
for (int i = 0; i < MAX_COLUMNS; i++)
{
if (i % PERF_CLMN == 1) continue;
...
}
这将导致工作不平衡,具体取决于 MAX_COLUMNS
是否是线程数的倍数。但是还有另一个来源或不平衡,即 rank_function()
的条件评估。因此,您应该利用动态调度。
现在,显然两个 array_dist*
循环都是私有的,它们不是您的情况,这将导致数据竞争。要么在循环体内移动数组的定义,要么使用 private()
子句。
#pragma omp parallel for schedule(dynamic) private(array_dist_perf,array_dist)
for (int i = 0; i < MAX_COLUMNS; i++)
{
if (i % PERF_CLMN == 1) continue;
...
}
现在,对于一些未经请求的优化建议:两个内部循环是多余的,因为第一个基本上是第二个工作的子集。您可以通过仅使用单个数组来优化计算并节省内存,并让第二个循环从第一个循环结束的地方继续。代码的最终版本应如下所示:
else if (PERF_ROWS <= MAX_ROWS && function_switch == true)
{
int array_dist[MAX_ROWS];
#pragma omp parallel for schedule(dynamic) private(array_dist)
for (int i = 0; i < MAX_COLUMNS; i++)
{
if (i % PERF_CLMN == 1) continue;
for (int j = 0; j < PERF_ROWS; j++) //truncation perforation
{
array_dist[j] = abs(input[j] - input_matrix[j][i]);
}
float av = mean(PERF_ROWS, array_dist);
float score = score_func(av);
if (score > THRESHOLD_SCORE)
{
for (int k = PERF_ROWS; k < MAX_ROWS; k++)
{
array_dist[k] = abs(input[k] - input_matrix[k][i]);
}
float av_real = mean(MAX_ROWS, array_dist);
float score_real = score_func(av_real);
rank_function(score_real, i);
}
}
}
另一个优化潜力在于 input_matrix
不是以缓存友好的方式访问的。转置它会导致列数据在内存中连续存储并改善内存访问局部性。