【IT168 文档】在CUDA编程中,结构的对齐(alignment)是非常重要的。在我写的CUDA 结构对齐的C++模拟这篇文章中,我介绍了如何在C++里模拟CUDA对结构的内存对齐机制,从而使得我们可以在C++里构造结构,然后传到CUDA里进行处理。但是随后我又遇到一个问题。比如,我们有这么一个对齐了的结构:
struct ALIGN16 Foo
{
ValueType x[Len];
...
};
当我们程序里使用了std::vector< Foo<5, float> > v(1);,也就是构造一个内存自管理的数组,并设置其初始大小为1。这段代码在g++里能够正常编译,但当我们用msvc(我用的是vc80)来编译的话,则会遇到报错:
TDCALL /O2 /MD /I. /c test\base\test_carray.cpp /Fotest\base\test_carray.obj
test_carray.cpp
C:\Program Files\Microsoft Visual Studio 8\VC\INCLUDE\vector(694) : error C2719:
'_Val': formal parameter with __declspec(align('16')) won't be aligned
test\base\test_carray.cpp(278) : see reference to class template instant
iation 'std::vector<_Ty>' being compiled
with
[
_Ty=carray_type
]
scons: *** [test\base\test_carray.obj] Error 2
scons: building terminated because of errors.
我们搜索STL vector的694行,发现有这样的代码:
{ // determine new length, padding with _Val elements as needed
if (size() < _Newsize)
_Insert_n(end(), _Newsize - size(), _Val);
else if (_Newsize < size())
erase(begin() + _Newsize, end());
}
问题就出在其第二个参数上,我们可以看到,这是个传值的参数,需要在函数调用的参数栈上创建一个临时的参数,但当这个类型是被对齐了的话,msvc编译器就会报错。
那么,怎么解决呢?直接修改STL代码,显然不是一个好方法。这时我想到了模板的特例化,可以用这种非侵入式的改变来解决这个问题,同时保证不修改STL代码。
首先,我们创建一个新的文件,比如叫foo_vector.hpp,并大致写成:
#define FOO_VECTOR_HPP
// only msvc need to be fix
#ifdef _MSC_VER // 1400 for vc80
#include <vector>
#include "foo.hpp"
namespace std
{
// our specialization goes here...
}
#endif // _MSC_VER
#endif
这里,我们首先判断编译器,如果是msvc,才进行我们的特例化,因为g++没有这个问题(并且我发现,msvc使用的STL代码和g++使用的STL代码是不相同的)。这里我没有进行msvc版本的判断,因为是demo嘛。
然后打开msvc自带的STL vector代码(在”Microsoft Visual Studio 8\VC\include\vector”),将整个vector类的定义拷贝过来,大致在421行到1246行之间。然后修改模板的头,改为我们的特例化:
template<unsigned int Len, typename ValueType,
class _Ax>
class vector < Foo<Len, ValueType>, _Ax >
: public _Vector_val<Foo<Len, ValueType>, _Ax>
{ // varying size array of values
public:
typedef Foo<Len, ValueType> _Ty;
typedef vector<_Ty, _Ax> _Myt;
typedef _Vector_val<_Ty, _Ax> _Mybase;
.......
这样,我们就完成了vector对我们Foo结构的特例化,接下来找到刚才产生问题的resize()方法,改为:
{ // determine new length, padding with _Val elements as needed
if (size() < _Newsize)
_Insert_n(end(), _Newsize - size(), _Val);
else if (_Newsize < size())
erase(begin() + _Newsize, end());
}
注意其中红色部分,将传值改为传参,避免在参数栈上创建被对齐的结构的对象。然后,在我们使用std::vector< Foo<5, float> >之前包含我们的foo_vector.hpp头文件,就可以正常使用了。