缺少 SIMD 指令的 OpenMP if 子句
Lack of OpenMP if clause for SIMD directive
我正在开发一些并行 C++ 模拟代码,我希望尽可能有效地对其进行矢量化。这就是我同时使用模板参数和 OpenMP SIMD 指令的原因:
- 此处的模板参数是为了解决最关键循环中可能出现的一些情况,方法是在编译时解决它们并完全删除相应的分支。
- OpenMP SIMD 指令强制编译器生成矢量化代码。
我的意思的一个(愚蠢的)例子如下:
template< bool checkNeeded >
int ratio( double *res, double *num, double *denom, int n ) {
#pragma omp simd
for ( int i = 0; i < n; i++ ) {
if ( checkNeeded ) { // dead code removed by the compiler when template is false
if ( denom == 0 ) {
std::cout << "Houston, we've got a problem\n";
return i;
}
}
res[i] = num[i] / denom[i];
}
return n;
}
在全球范围内,它工作得很好,但我遇到的麻烦是,在(非常罕见的)我想使用代码的 ratio<true>()
版本的情况下,这个代码已经被编译器矢量化了因为 #pragma omp simd
指令,由于测试、打印和提前退出循环,它比非矢量化版本慢得多……
所以我需要的是在我的 simd
指令中添加一个 if
子句,指示编译器何时服从该指令。那会给出这样的东西:
#pragma omp simd if( checkNeeded == false )
不幸的是,尽管许多 OpenMP 指令都支持这样的 if
子句,但 simd
不支持...我不认为我的请求是完全愚蠢的,所以我想知道为什么是这样吗,以后有没有可能支持。
有人知道吗?
扩展 user3528438 的评论,这可能是将您的函数拆分为两个不同函数的最合乎逻辑的地方之一。一个处理 false
情况并按照您的方式编写,另一个处理 true
情况并且没有 simd 命令。
或者,如果你坚持使用一个函数,你可以很容易地写
template< bool checkNeeded >
int ratio( double *res, double *num, double *denom, int n ) {
if (!checkNeeded) {
#pragma omp simd
for ( int i = 0; i < n; i++ ) {
res[i] = num[i] / denom[i];
}
return n;
} else {
for ( int i = 0; i < n; i++ ) {
if ( denom == 0 ) {
std::cout << "Houston, we've got a problem\n";
return i;
}
res[i] = num[i] / denom[i];
}
return n;
}
}
这将比 false
情况下的初始函数稍慢,因为有一个 if 语句要评估(假设 n
很大 [甚至大于 10 而你不是一个重要因素)不应该注意到减速])。此外,在 true
情况下它会快得多,因为您不必在每次迭代时都评估第一个 if 语句。
template< bool checkNeeded >
int ratio( double *res, double *num, double *denom, int n );// c++ declaration
template<>int ratio<true>( double *res, double *num, double *denom, int n )
{
for ( int i = 0; i < n; i++ ) {
res[i] = num[i] / denom[i];
}
return n;
}
template<>int ratio<false>( double *res, double *num, double *denom, int n ) {
for ( int i = 0; i < n; i++ ) {
if ( denom == 0 ) {
std::cout << "Houston, we've got a problem\n";
return i;
}
res[i] = num[i] / denom[i];
}
return n;
}
How do I explicitly instantiate a template function?
I don't think my request is completely stupid so I wonder why it is
so, and whether it is likely to be supported in the future. Anybody
knows about that?
SIMD 指令会影响编译时的代码生成,而其他 OpenMP 构造上的 "if" 子句会实现 运行 时测试。 ("if" 条件不是编译时常量)。因此,要在 SIMD 子句上实现 "if",通常需要编译器克隆循环体并生成两个不同的版本,然后选择哪个在 运行 时动态执行。
对于一个非常罕见的案例来说,这似乎需要付出很多努力,所以我怀疑它是否会成为标准。 (而且,无论如何,在这一点上,您可以寻找的第一个标准在几年内不会出现,因此您可能需要更实用的修复:-))
我正在开发一些并行 C++ 模拟代码,我希望尽可能有效地对其进行矢量化。这就是我同时使用模板参数和 OpenMP SIMD 指令的原因:
- 此处的模板参数是为了解决最关键循环中可能出现的一些情况,方法是在编译时解决它们并完全删除相应的分支。
- OpenMP SIMD 指令强制编译器生成矢量化代码。
我的意思的一个(愚蠢的)例子如下:
template< bool checkNeeded >
int ratio( double *res, double *num, double *denom, int n ) {
#pragma omp simd
for ( int i = 0; i < n; i++ ) {
if ( checkNeeded ) { // dead code removed by the compiler when template is false
if ( denom == 0 ) {
std::cout << "Houston, we've got a problem\n";
return i;
}
}
res[i] = num[i] / denom[i];
}
return n;
}
在全球范围内,它工作得很好,但我遇到的麻烦是,在(非常罕见的)我想使用代码的 ratio<true>()
版本的情况下,这个代码已经被编译器矢量化了因为 #pragma omp simd
指令,由于测试、打印和提前退出循环,它比非矢量化版本慢得多……
所以我需要的是在我的 simd
指令中添加一个 if
子句,指示编译器何时服从该指令。那会给出这样的东西:
#pragma omp simd if( checkNeeded == false )
不幸的是,尽管许多 OpenMP 指令都支持这样的 if
子句,但 simd
不支持...我不认为我的请求是完全愚蠢的,所以我想知道为什么是这样吗,以后有没有可能支持。
有人知道吗?
扩展 user3528438 的评论,这可能是将您的函数拆分为两个不同函数的最合乎逻辑的地方之一。一个处理 false
情况并按照您的方式编写,另一个处理 true
情况并且没有 simd 命令。
或者,如果你坚持使用一个函数,你可以很容易地写
template< bool checkNeeded >
int ratio( double *res, double *num, double *denom, int n ) {
if (!checkNeeded) {
#pragma omp simd
for ( int i = 0; i < n; i++ ) {
res[i] = num[i] / denom[i];
}
return n;
} else {
for ( int i = 0; i < n; i++ ) {
if ( denom == 0 ) {
std::cout << "Houston, we've got a problem\n";
return i;
}
res[i] = num[i] / denom[i];
}
return n;
}
}
这将比 false
情况下的初始函数稍慢,因为有一个 if 语句要评估(假设 n
很大 [甚至大于 10 而你不是一个重要因素)不应该注意到减速])。此外,在 true
情况下它会快得多,因为您不必在每次迭代时都评估第一个 if 语句。
template< bool checkNeeded >
int ratio( double *res, double *num, double *denom, int n );// c++ declaration
template<>int ratio<true>( double *res, double *num, double *denom, int n )
{
for ( int i = 0; i < n; i++ ) {
res[i] = num[i] / denom[i];
}
return n;
}
template<>int ratio<false>( double *res, double *num, double *denom, int n ) {
for ( int i = 0; i < n; i++ ) {
if ( denom == 0 ) {
std::cout << "Houston, we've got a problem\n";
return i;
}
res[i] = num[i] / denom[i];
}
return n;
}
How do I explicitly instantiate a template function?
I don't think my request is completely stupid so I wonder why it is so, and whether it is likely to be supported in the future. Anybody knows about that?
SIMD 指令会影响编译时的代码生成,而其他 OpenMP 构造上的 "if" 子句会实现 运行 时测试。 ("if" 条件不是编译时常量)。因此,要在 SIMD 子句上实现 "if",通常需要编译器克隆循环体并生成两个不同的版本,然后选择哪个在 运行 时动态执行。
对于一个非常罕见的案例来说,这似乎需要付出很多努力,所以我怀疑它是否会成为标准。 (而且,无论如何,在这一点上,您可以寻找的第一个标准在几年内不会出现,因此您可能需要更实用的修复:-))