OpenMP为内联函数声明SIMD
OpenMP declare SIMD for an inline function
当前的OpenMP标准介绍了C/C++的declare simd
指令:
在函数上使用declare-simd构造可以创建可用于在SIMD循环中处理来自单个调用的多个参数同时
本章给出了更多的细节,但指令可以应用的功能类型似乎没有限制。
所以我的问题是,这个指令可以安全地应用于inline
函数吗?
我这么问有两个原因:
inline
函数是一个非常不寻常的函数,因为它通常直接内联在被调用的地方。因此,它可能从未被编译为独立函数,因此,它的declare simd
方面与封闭循环级别的可能的simd
指令是相当冗余的- 我有一个带有
inline
declare simd
函数的代码,有时,由于一些模糊的原因,GCC会抱怨它们在链接时有多个定义(名称中有多余的字符,表明这些是矢量化版本)。但如果我删除declare simd
指令,它会编译并链接良好
到目前为止,我还没有想太多,但现在我很困惑。这是我的错误吗(即对inline
函数使用declare simd
),还是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;
}
模拟立方厘米:
#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的评论和Z玻色子的问题让我感到欣慰,因为我的代码符合OpenMP,这是GCC中的一个错误。我会用我能找到的最新版本做进一步的测试,并在需要时报告。
内联函数是一个非常不寻常的函数,因为它通常直接在被调用的地方内联。因此,它可能从未被编译为独立函数。
这是不正确的。除非声明为static
,否则带或不带内联的函数都具有外部链接。编译器必须生成该函数的独立版本(不会内联),以防从另一个对象文件调用该函数。如果您不想要一个独立的函数,请声明函数static
。有关更多详细信息,请参阅Agner Fog的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
CCD_ 20给出了相同的结果。
大写的"W"answers"T"表示符号是外部的。然而,"W"是不会引起链路错误的弱符号,而"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版本生成强符号,所以我不能完全回答你的问题,但我认为这些信息仍然很有趣。
- .cpp和.h文件中的模板专用化声明
- 未在作用域中声明unordered_map
- C++避免重复声明的语法是什么
- 如何确保C++函数在定义之前声明(如override关键字)
- 错误:未在此范围内声明'reverse'
- 奇怪的(对我来说)返回声明 - 在谷歌上找不到任何关于它的信息
- 为什么在定义函数之前先声明它
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- #ifdef和未声明的标识符
- 没有显式声明的int[]中的foreach
- 在基于范围的for循环中使用结构化绑定声明
- 在将变量声明为引用时,堆在释放后使用
- C++:无法访问声明的受保护成员
- 为什么我不能在一个类的不同行中声明和定义成员变量?
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 使用cmake从源代码构建MySQL连接器/C++失败(与以前的声明冲突)
- 在函数内部的声明中初始化数组,并在外部使用它
- Visual Studio中的函数声明和函数定义问题
- c++类声明时,相同的例程,不同的成员变量类型
- OpenMP为内联函数声明SIMD