current OpenMP standard说的是关于C / C ++的declare simd
指令:
在函数上使用declare simd构造可以创建 可用于相关功能的SIMD版本 处理SIMD循环中单个调用的多个参数 同时进行。
本章给出了更多细节,但似乎对该指令可以应用的函数类型没有限制。
所以我的问题是,这个指令可以安全地应用于inline
函数吗?
我问这有两个原因:
inline
函数是一个相当不寻常的函数,因为它通常直接在调用它的位置内联。因此,它可能永远不会编译为独立函数,因此,它的declare simd
方面与封闭循环级别的可能simd
指令相当多余。inline
declare simd
个函数的代码,有时候,由于一些模糊的原因,GCC抱怨他们在链接时的多重定义(名称被额外的字符修改,表明这些是矢量化的版本)。但是如果我删除declare simd
指令,它就会编译并链接正常。到目前为止,我没有想太多,但现在我很困惑。这是我的错误(即declare simd
使用inline
函数)还是GCC生成inline
函数的二进制矢量化版本并且在链接时无法对其进行排序的问题?
修改
有一个GCC编译器选项可以带来改变。启用内联时(例如-O3
),代码编译并链接正常。但是当使用-O0
或-O3 -fno-inline
进行编译时,内联将被禁用,并且链接将失败,并使用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编译得很好。
最终修改:
Hristo Iliev's comment和Z boson's question让我感到安慰,因为我的代码符合OpenMP标准,而且这是GCC中的一个错误。我会看到使用我能找到的最新版本进行进一步测试,并在需要时报告。
答案 0 :(得分:1)
内联函数是一个相当不寻常的函数,因为它通常直接在调用它的位置内联。因此它很可能永远不会编译为独立函数。
这是不正确的。除非声明static
具有外部链接,否则包含或不包含内联的函数。如果从另一个目标文件调用该函数,编译器必须生成该函数的独立版本(不会内联)。如果您不希望独立函数声明函数static
。有关更多详细信息,请参阅Agner Fog的Optimizing software in C++中的第8.3节和“内联函数具有非内联副本”标题。
使用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,它不会导致链接错误,但“T”是强符号。这就说明了链接器抱怨的原因。
static inline
的结果是什么?在这种情况下nm foobar.o | grep foo
给出
t _ZGVbM2v__ZL3food
t _ZGVbN2v__ZL3food
t _ZL3food
和nm simd.o | grep foo
给出了同样的东西。但是小写“t”表示符号具有局部链接,因此链接器没有问题。
如果我们在没有OpenMP的情况下编译,则生成的唯一foo
符号为_ZL3food
。我不知道为什么GCC会为非SIMD版本的功能和SIMD版本的强符号生成弱符号,所以我不能完全回答你的问题,但我认为这些信息仍然很有趣。