我是OpenMP的新手,但我正在尝试使用它来加速对具有大量行和少量列的2D数组条目的某些操作。同时,我使用简化来计算每列中所有数组值的总和。代码看起来像这样(我将在片刻解释奇怪的位):
template <unsigned int NColumns>
void Function(int n_rows, double** X, double* Y)
{
#pragma omp parallel for reduction(+:Y[:NColumns])
for (int r = 0; r < n_rows; ++r)
{
for (int c = 0; c < NColumns; ++c)
{
X[r][c] = some_complicated_stuff(X[r], X[r][c]);
Y[c] += X[r][c];
}
}
}
为了澄清,X是在堆上分配的n_rows
x NColumns
大小的2D数组,Y是NColumns
大小的1D数组。 some_complicated_stuff
实际上并未作为单独的函数实现,但我对该行中的X[r][c]
所做的只取决于X[r][c]
和1D数组X[r]
中的其他值。< / p>
NColumns
作为模板参数而不是作为常规参数(如n_rows
)传入的原因是,在编译时知道NColumns
时,编译器可以在上述功能中更积极地优化内循环。我知道程序运行时NColumns
将成为少数值之一,所以稍后我会有类似这样的代码:
cin >> n_cols;
double** X;
double Y[n_cols];
// initialise X and Y, etc. . .
for (int i = 0; i < n_iterations; ++i)
{
switch (n_cols)
{
case 2: Function< 2>(X, Y); break;
case 10: Function<10>(X, Y); break;
case 60: Function<60>(X, Y); break;
default: throw "Unsupported n_cols."; break;
}
// . . .
Report(i, Y); // see GDB output below
}
通过测试,我发现将此NColumns
“参数”更新为模板参数而非正常函数参数实际上会使性能明显提高。然而,我也发现,一旦在一个蓝色的月亮(比如大约每10 ^ 7次调用Function
),程序就会挂起 - 更糟糕的是,它的行为有时会从程序的一次运行变为下一个。这种情况很少发生,我在隔离错误方面遇到了很多麻烦,但我现在想知道是不是因为我在OpenMP减少中使用了这个NColumns
模板参数。
我注意到similar StackOverflow question询问在缩减中使用模板类型,这显然会导致未指定的行为 - OpenMP 3.0 spec说
如果数据共享属性子句中引用的变量具有类型 从模板派生,并没有其他参考 程序中的变量,那么与该变量相关的任何行为都是 未指定的。
在这种情况下,它不是正在使用的模板类型本身,但我在同一个球场。我在这里搞砸了,或者错误更可能出现在代码的其他部分?
我正在使用GCC 6.3.0。
如果它更有帮助,这里是来自Function
内部的真实代码。 X
实际上是一个扁平的2D数组;其他地方定义了ww
和min_x
:
#pragma omp parallel for reduction(+:Y[:NColumns])
for (int i = 0; i < NColumns * n_rows; i += NColumns)
{
double total = 0;
for (int c = 0; c < NColumns; ++c)
if (X[i + c] > 0)
total += X[i + c] *= ww[c];
if (total > 0)
for (int c = 0; c < NColumns; ++c)
if (X[i + c] > 0)
Y[c] += X[i + c] = (X[i + c] < min_x * total ? 0 : X[i + c] / total);
}
为了稍微加厚一下情节,我将gdb附加到程序的正在运行的程序中,这就是回溯显示给我的内容:
#0 0x00007fff8f62a136 in __psynch_cvwait () from /usr/lib/system/libsystem_kernel.dylib
#1 0x00007fff8e65b560 in _pthread_cond_wait () from /usr/lib/system/libsystem_pthread.dylib
#2 0x000000010a4caafb in omp_get_num_procs () from /opt/local/lib/libgcc/libgomp.1.dylib
#3 0x000000010a4cad05 in omp_get_num_procs () from /opt/local/lib/libgcc/libgomp.1.dylib
#4 0x000000010a4ca2a7 in omp_in_final () from /opt/local/lib/libgcc/libgomp.1.dylib
#5 0x000000010a31b4e9 in Report(int, double*) ()
#6 0x3030303030323100 in ?? ()
[snipped traces 7-129, which are all ?? ()]
#130 0x0000000000000000 in ?? ()
Report()
是一个在程序主循环内调用但不在Function()
内的函数(我已将其添加到上面的中间代码片段中),Report()
不包含任何OpenMP pragma。这是否能说明发生了什么?
请注意,可执行文件在进程开始运行和我将GDB连接到它之间进行了更改,这需要引用新的(已更改的)可执行文件。这可能意味着符号表搞砸了。
答案 0 :(得分:0)
我已经成功解决了这个问题。
其中一个问题是程序表现不确定。这只是因为(1)OpenMP执行线程完成顺序的减少,这是非确定性的,并且(2)浮点加法是非关联的。我假设减少将以线号顺序执行,但事实并非如此。因此,只要线程数大于2,即使从一次运行到下一次运行的线程数相同,任何减少使用浮点运算的OpenMP for
构造都可能是不确定的。有关此问题的一些相关StackOverflow问题是here和here。
另一个问题是该程序偶尔会挂起。我无法解决这个问题。在挂起进程上运行gdb
始终会在堆栈跟踪的顶部生成__psynch_cvwait ()
。它挂起了并行化for循环的每10 ^ 8次执行。
希望这有点帮助。