OpenMP 为内联函数声明 SIMD
OpenMP declare SIMD for an inline function
current OpenMP standard 关于 C/C++ 的 declare simd
指令:
The use of a declare simd construct on a function enables the creation
of SIMD versions of the associated function that can be used to
process multiple arguments from a single invocation in a SIMD loop
concurrently.
本章中给出了更多详细信息,但似乎对该指令可以应用的函数类型没有限制。
所以我的问题是,这个指令可以安全地应用于 inline
函数吗?
我问这个有两个原因:
inline
函数是一个相当不寻常的函数,因为它通常直接内联在调用它的地方。所以它可能永远不会被编译为一个独立的函数,因此,它的 declare simd
方面对于封闭循环级别的可能 simd
指令是非常多余的。
- 我有一个带有
inline
declare simd
函数的代码,有时,出于某些模糊的原因,GCC 在 link 时抱怨它们的多重定义(名称被额外的破坏暗示这些是矢量化版本的字符)。但是如果我删除 declare simd
指令,它会编译并且 link 没问题。
到目前为止我还没有想太多,但现在我很困惑。这是我的错误(即对 inline
函数使用 declare simd
)还是 GCC 生成 inline
函数的二进制矢量化版本并且未能在 [=89 处对它们进行排序时出现问题=]时间?
编辑:
有一个 GCC 编译器选项有所不同。当启用内联时(例如 -O3
),代码编译并且 link 没问题。但是当使用 -O0
或 -O3 -fno-inline
编译时,内联被禁用并且 linking 失败,这个 "multiple definition of" 用 omp declare simd
指令修饰的函数。
编辑 2:
感谢@Zboson 关于编译器标志的问题,我设法创建了一个复制器。这是:
foobar.h:
#ifndef FOOBAR_H_
#define FOOBAR_H_
#include <cmath>
#pragma omp declare simd
inline double foo( double d ) {
return sin( cos( exp( d ) ) );
}
double bar( double *v, int len );
#endif
foobar.cc:
#include "foobar.h"
double bar( double *v, int len ) {
double sum = 0;
for ( int i = 0; i < len; i++ ) {
sum += foo( v[i] );
}
return sum;
}
simd.cc:
#include <iostream>
#include "foobar.h"
int main() {
const int len = 100;
double *v = new double[len];
for ( int i = 0; i < len; i++ ) {
v[i] = i;
}
double sum = 0;
#pragma omp simd reduction( +: sum )
for ( int i = 0; i < len; i++ ) {
sum += foo( v[i] );
}
std::cout << sum << " " << bar( v, len ) << std::endl;
delete[] v;
return 0;
}
编译:
> g++ -fopenmp -g simd.cc foobar.cc
/tmp/ccI4e7ip.o: In function `_ZGVbN2v__Z3food':
foobar.h:7: multiple definition of `_ZGVbN2v__Z3food'
/tmp/cc4U8Qyu.o:foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVbM2v__Z3food':
foobar.h:7: multiple definition of `_ZGVbM2v__Z3food'
/tmp/cc4U8Qyu.o:foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVcN4v__Z3food':
foobar.h:7: multiple definition of `_ZGVcN4v__Z3food'
/tmp/cc4U8Qyu.o:foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVcM4v__Z3food':
foobar.h:7: multiple definition of `_ZGVcM4v__Z3food'
foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVdN4v__Z3food':
foobar.h:7: multiple definition of `_ZGVdN4v__Z3food'
foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVdM4v__Z3food':
foobar.h:7: multiple definition of `_ZGVdM4v__Z3food'
foobar.h:7: first defined here
collect2: error: ld returned 1 exit status
> c++filt _ZGVdM4v__Z3food
_ZGVdM4v__Z3food
> c++filt _Z3food
foo(double)
Gcc 版本 4.9.2 和 5.1.0 都给出了完全相同的问题,而英特尔编译器版本 15.0.3 编译它就好了。
最终编辑:
and 安慰我一下,我的代码是 OpenMP 兼容的,这是 GCC 中的一个错误。我会使用我能找到的最新版本进行进一步测试,并在需要时报告。
An inline function is a rather unusual function, since it is normally inlined directly in the place it was called. So it is likely never compiled as a standalone function.
这是不正确的。除非声明为 static
,否则带或不带内联的函数具有外部 linkage。编译器必须生成函数的 stand-alone 版本(不会内联),以防从另一个 object 文件调用该函数。如果您不想要独立函数,请声明函数 static
。有关详细信息,请参阅 Agner Fog Optimizing software in C++ 中的第 8.3 节和标题 "Inlined functions have a non-inlined copy"。
使用 static inline double foo
不会给您的代码带来错误。
现在让我们看看符号。不使用 static
nm foobar.o | grep foo
给予
W _Z3food
T _ZGVbM2v__Z3food
T _ZGVbN2v__Z3food
T _ZGVcM4v__Z3food
T _ZGVcN4v__Z3food
T _ZGVdM4v__Z3food
T _ZGVdN4v__Z3food
和nm foobar.o | grep foo
给出了同样的东西。
大写的"W"和"T"表示符号是外部的。但是 "W" 是一个 weak symbol,它不会导致 link 错误,但是 "T" 是一个强符号,它会导致错误。所以这就是 linker 抱怨的原因。
static inline
的结果是什么?在这种情况下 nm foobar.o | grep foo
给出
t _ZGVbM2v__ZL3food
t _ZGVbN2v__ZL3food
t _ZL3food
和 nm simd.o | grep foo
给出了同样的东西。但是小写 "t" 表示符号具有本地 linkage,因此 linker.
没有问题
如果我们在没有 OpenMP 的情况下进行编译,则生成的唯一 foo
符号是 _ZL3food
。我不知道为什么 GCC 为 non-SIMD 版本的函数生成弱符号,为 SIMD 版本生成强符号,所以我不能完全回答你的问题,但我认为这些信息仍然很有趣。
current OpenMP standard 关于 C/C++ 的 declare simd
指令:
The use of a declare simd construct on a function enables the creation of SIMD versions of the associated function that can be used to process multiple arguments from a single invocation in a SIMD loop concurrently.
本章中给出了更多详细信息,但似乎对该指令可以应用的函数类型没有限制。
所以我的问题是,这个指令可以安全地应用于 inline
函数吗?
我问这个有两个原因:
inline
函数是一个相当不寻常的函数,因为它通常直接内联在调用它的地方。所以它可能永远不会被编译为一个独立的函数,因此,它的declare simd
方面对于封闭循环级别的可能simd
指令是非常多余的。- 我有一个带有
inline
declare simd
函数的代码,有时,出于某些模糊的原因,GCC 在 link 时抱怨它们的多重定义(名称被额外的破坏暗示这些是矢量化版本的字符)。但是如果我删除declare simd
指令,它会编译并且 link 没问题。
到目前为止我还没有想太多,但现在我很困惑。这是我的错误(即对 inline
函数使用 declare simd
)还是 GCC 生成 inline
函数的二进制矢量化版本并且未能在 [=89 处对它们进行排序时出现问题=]时间?
编辑:
有一个 GCC 编译器选项有所不同。当启用内联时(例如 -O3
),代码编译并且 link 没问题。但是当使用 -O0
或 -O3 -fno-inline
编译时,内联被禁用并且 linking 失败,这个 "multiple definition of" 用 omp declare simd
指令修饰的函数。
编辑 2:
感谢@Zboson 关于编译器标志的问题,我设法创建了一个复制器。这是:
foobar.h:
#ifndef FOOBAR_H_
#define FOOBAR_H_
#include <cmath>
#pragma omp declare simd
inline double foo( double d ) {
return sin( cos( exp( d ) ) );
}
double bar( double *v, int len );
#endif
foobar.cc:
#include "foobar.h"
double bar( double *v, int len ) {
double sum = 0;
for ( int i = 0; i < len; i++ ) {
sum += foo( v[i] );
}
return sum;
}
simd.cc:
#include <iostream>
#include "foobar.h"
int main() {
const int len = 100;
double *v = new double[len];
for ( int i = 0; i < len; i++ ) {
v[i] = i;
}
double sum = 0;
#pragma omp simd reduction( +: sum )
for ( int i = 0; i < len; i++ ) {
sum += foo( v[i] );
}
std::cout << sum << " " << bar( v, len ) << std::endl;
delete[] v;
return 0;
}
编译:
> g++ -fopenmp -g simd.cc foobar.cc
/tmp/ccI4e7ip.o: In function `_ZGVbN2v__Z3food':
foobar.h:7: multiple definition of `_ZGVbN2v__Z3food'
/tmp/cc4U8Qyu.o:foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVbM2v__Z3food':
foobar.h:7: multiple definition of `_ZGVbM2v__Z3food'
/tmp/cc4U8Qyu.o:foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVcN4v__Z3food':
foobar.h:7: multiple definition of `_ZGVcN4v__Z3food'
/tmp/cc4U8Qyu.o:foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVcM4v__Z3food':
foobar.h:7: multiple definition of `_ZGVcM4v__Z3food'
foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVdN4v__Z3food':
foobar.h:7: multiple definition of `_ZGVdN4v__Z3food'
foobar.h:7: first defined here
/tmp/ccI4e7ip.o: In function `_ZGVdM4v__Z3food':
foobar.h:7: multiple definition of `_ZGVdM4v__Z3food'
foobar.h:7: first defined here
collect2: error: ld returned 1 exit status
> c++filt _ZGVdM4v__Z3food
_ZGVdM4v__Z3food
> c++filt _Z3food
foo(double)
Gcc 版本 4.9.2 和 5.1.0 都给出了完全相同的问题,而英特尔编译器版本 15.0.3 编译它就好了。
最终编辑:
An inline function is a rather unusual function, since it is normally inlined directly in the place it was called. So it is likely never compiled as a standalone function.
这是不正确的。除非声明为 static
,否则带或不带内联的函数具有外部 linkage。编译器必须生成函数的 stand-alone 版本(不会内联),以防从另一个 object 文件调用该函数。如果您不想要独立函数,请声明函数 static
。有关详细信息,请参阅 Agner Fog Optimizing software in C++ 中的第 8.3 节和标题 "Inlined functions have a non-inlined copy"。
使用 static inline double foo
不会给您的代码带来错误。
现在让我们看看符号。不使用 static
nm foobar.o | grep foo
给予
W _Z3food
T _ZGVbM2v__Z3food
T _ZGVbN2v__Z3food
T _ZGVcM4v__Z3food
T _ZGVcN4v__Z3food
T _ZGVdM4v__Z3food
T _ZGVdN4v__Z3food
和nm foobar.o | grep foo
给出了同样的东西。
大写的"W"和"T"表示符号是外部的。但是 "W" 是一个 weak symbol,它不会导致 link 错误,但是 "T" 是一个强符号,它会导致错误。所以这就是 linker 抱怨的原因。
static inline
的结果是什么?在这种情况下 nm foobar.o | grep foo
给出
t _ZGVbM2v__ZL3food
t _ZGVbN2v__ZL3food
t _ZL3food
和 nm simd.o | grep foo
给出了同样的东西。但是小写 "t" 表示符号具有本地 linkage,因此 linker.
如果我们在没有 OpenMP 的情况下进行编译,则生成的唯一 foo
符号是 _ZL3food
。我不知道为什么 GCC 为 non-SIMD 版本的函数生成弱符号,为 SIMD 版本生成强符号,所以我不能完全回答你的问题,但我认为这些信息仍然很有趣。