opencl:C++11下使用别名(x,y,z,hi,lo...)访问vector类型(cl_int2,cl_long16...)的元素

来源:互联网 发布:重庆seo服务 编辑:程序博客网 时间:2024/06/02 00:52

在gcc(5.2.0)下使用C++11写opencl的主机端代码时,发现无法像内核代码一样对cl_int2这样的向量(vector)类型用pos.x,pos.y这样的别名来访问向量元素,只能用pos.s[0]这种数组访问的方式。这是为什么?
这本是个小问题,但本人是个完美主义者,总想搞个清楚,最后总算搞清楚了,于是就有了本文。

这是platform.hcl_int2的定义,可以看出,虽然代码中有,x,y名字定义,但编译开关__CL_HAS_ANON_STRUCT__导致这部分代码是灰的/无效的
这里写图片描述


opencl内核代码中向量元素的访问

在opencl内核代码中,对于opencl中的向量类型,既可以使用s0~sF(根据向量长度不同)来访问向量中的指定元素,也可以用元素的别名来访问(x,y,z,w,hi,lo…)
比如向量数据float4 ,是由4个float组成的向量

float4 f;float s0=f.s0; //f中第一个元素float s0=f.x;  //与前一行等价float2 f2=f.hi //f中前2个元素组成的float2

可以看出,使用x,y,hi,lo这样的别名,代码更加直观易懂。

opencl主机端向量类型的定义

这些向量类型在主机端都有等价的向量类型定义,区别就是类型名字加了cl_前缀,如内核代码中int2类型在主机端是cl_int2,内核代码中float4类型在主机端是cl_float4
参见下面的cl_float4的定义:

typedef union{    cl_float  CL_ALIGNED(16) s[4];#if __CL_HAS_ANON_STRUCT__   __CL_ANON_STRUCT__ struct{ cl_float   x, y, z, w; };   __CL_ANON_STRUCT__ struct{ cl_float   s0, s1, s2, s3; };   __CL_ANON_STRUCT__ struct{ cl_float2  lo, hi; };#endif#if defined( __CL_FLOAT2__)     __cl_float2     v2[2];#endif#if defined( __CL_FLOAT4__)     __cl_float4     v4;#endif}cl_float4;// 摘自cl_platform.h

从上面cl_float4的定义可以看出主机端的cl_float4是个联合体,默认是以数字下标访问向量元素的(s[0],s1,s[2],s[3])。同时它也支持以别名(x,y,z,w,s0~s3)访问元素。

编译器差异

不过你也看到了这些别名都定义在匿名结构体(anonymous struct)中,而匿名结构体并不是C语言标准的一部分,是编译器自行实现的,所以__CL_HAS_ANON_STRUCT__宏开关决定编译器是否支持匿名结构体(anonymous struct),控制着是否允许使用别名访问元素。
于是我顺藤摸瓜找到__CL_HAS_ANON_STRUCT__定义的位置,就是下面这段代码(中文部分是作者加的注释)

/* Define capabilities for anonymous struct members. */#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ )// gcc下如果没定义__STRICT_ANSI__,则__CL_HAS_ANON_STRUCT__为1#define  __CL_HAS_ANON_STRUCT__ 1#define  __CL_ANON_STRUCT__ __extension__#elif defined( _WIN32) && (_MSC_VER >= 1500)// VS2008以后支持匿名结构体,但会有警告,所以这里会有关闭C4201警告   /* Microsoft Developer Studio 2008 supports anonymous structs, but    * complains by default. */#define  __CL_HAS_ANON_STRUCT__ 1#define  __CL_ANON_STRUCT__   /* Disable warning C4201: nonstandard extension used : nameless    * struct/union */#pragma warning( push )#pragma warning( disable : 4201 )#else// gcc下如果定义了__STRICT_ANSI__,则__CL_HAS_ANON_STRUCT__为0#define  __CL_HAS_ANON_STRUCT__ 0#define  __CL_ANON_STRUCT__#endif// 摘自cl_platform.h

上面这段代码控制了__CL_HAS_ANON_STRUCT__ 的定义,可以看出,在使用gcc编译时,__CL_HAS_ANON_STRUCT__是否为1,取决于是否定义了__STRICT_ANSI__
如果定义了__STRICT_ANSI____CL_HAS_ANON_STRUCT__为0,否则为1。
也就是说,在gcc下编译,如果定义__STRICT_ANSI__就没办法使用别名访问向量元素。
下图就是我在Eclipse+MinGW(5.2.0)环境下打开cl_platform.h看到的__CL_HAS_ANON_STRUCT__ 的定义,说明__STRICT_ANSI__被定义了,
这里写图片描述

根本原因

那么接下来的问题就是:__STRICT_ANSI__是个什么鬼?
关于__STRICT_ANSI__来历,请参见我的上一篇博客《C++11:MinGW当指定-std=c++11选项时 默认定义了__STRICT_ANSI__》(其实本文的答案也隐藏在这篇博客里)

从这篇博客的标题就可以得知,如果编译代码时使用了-ansi选项,编译器就会定义__STRICT_ANSI__,我找遍了整个项目代码,确信没有使用过-ansi(太高端我从来不知道这个选项),所以并不是因为我使用了-ansi才造成这个问题,而是因为我使用了-std=c++11选项导致编译器自动定义了__STRICT_ANSI__

解决方案

知道了问题的根本原因,解决问题的办法也就有了。
方案1:
第一个办法就是前述博客中最后提到的办法:在使用-std=c++11选项的同时,加上-U__STRICT_ANSI__选项, 用于去掉__STRICT_ANSI__定义
如果你是用cmake来编译项目代码,可以在CMakeList.txt中加入这样的代码

#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持,并去掉__STRICT_ANSI__定义if(CMAKE_COMPILER_IS_GNUCXX)    add_compile_options(-std=c++11)    message(STATUS "optional:-std=c++11")       add_compile_options(-U__STRICT_ANSI__)    message(STATUS "optional:-U__STRICT_ANSI__")    endif(CMAKE_COMPILER_IS_GNUCXX)

方案二
修改你的源代码,在#include <CL/cl.hpp>#include <CL/opencl.h>语句之前使用#undef __STRICT_ANSI__删除__STRICT_ANSI__定义

#if defined( __GNUC__) && defined( __STRICT_ANSI__ )#define __STRICT_ANSI__DEFINED__//删除__STRICT_ANSI__定义#undef __STRICT_ANSI__  #endif#include <CL/cl.hpp>#ifdef __STRICT_ANSI__DEFINED__#undef __STRICT_ANSI__DEFINED__//恢复__STRICT_ANSI__定义#define __STRICT_ANSI__#endif

代码做上述修改后,重新rebuild index,
这里写图片描述

再打开cl_platform.h看到的__CL_HAS_ANON_STRUCT__ 的定义,说明__STRICT_ANSI__没有被定义,
这里写图片描述
这时再看cl_int的定义,也正常了
这里写图片描述

这两种解决方案,你可以根据自己的需要来选择,但第二种方案的没有副作用,不会影响项目中其他部分代码的编译。第一种方案会有潜在的副作用,就是可能会影响项目中与opencl无关的代码的编译。

0 0