1. 程式人生 > >具有 __declspec(align('16')) 的形參將不被對齊

具有 __declspec(align('16')) 的形參將不被對齊

 【IT168 文件】在CUDA程式設計中,結構的對齊(alignment)是非常重要的。在我寫的CUDA 結構對齊的C++模擬這篇文章中,我介紹瞭如何在C++裡模擬CUDA對結構的記憶體對齊機制,從而使得我們可以在C++裡構造結構,然後傳到CUDA裡進行處理。但是隨後我又遇到一個問題。比如,我們有這麼一個對齊了的結構:

template <unsigned int Len, typename ValueType>
struct ALIGN16 Foo
{
    ValueType x[Len];
    ...
};

  當我們程式裡使用了std::vector< Foo<5, float> > v(1);,也就是構造一個記憶體自管理的陣列,並設定其初始大小為1。這段程式碼在g++裡能夠正常編譯,但當我們用msvc(我用的是vc80)來編譯的話,則會遇到報錯:

cl /nologo /TP /EHsc /wd4819 /DBOOST_BIND_ENABLE_STDCALL /DBOOST_MEM_FN_ENABLE_S
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行,發現有這樣的程式碼:

void resize(size_type _Newsize, _Ty _Val)
        {    
// determine new length, padding with _Val elements as neededif (size() < _Newsize)
            _Insert_n(end(), _Newsize 
- size(), _Val);
        
elseif (_Newsize < size())
            erase(begin() 
+ _Newsize, end());
        }

  問題就出在其第二個引數上,我們可以看到,這是個傳值的引數,需要在函式呼叫的引數棧上建立一個臨時的引數,但當這個型別是被對齊了的話,msvc編譯器就會報錯。

  那麼,怎麼解決呢?直接修改STL程式碼,顯然不是一個好方法。這時我想到了模板的特例化,可以用這種非侵入式的改變來解決這個問題,同時保證不修改STL程式碼。

  首先,我們建立一個新的檔案,比如叫foo_vector.hpp,並大致寫成:

#ifndef 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 9.0\VC\include\vector”),將整個vector類的定義拷貝過來,大致在434行到1292行之間。然後修改模板的頭,改為我們的特例化:

// TEMPLATE CLASS vectortemplate<unsigned int Len, typename ValueType,class _Ax>class vector < Foo<Len, ValueType>, _Ax >
    : public _Vector_val<Foo<Len, ValueType>, _Ax>
{    
// varying size array of valuespublic:
    
typedef Foo<Len, ValueType> _Ty;
    typedef vector
<_Ty, _Ax> _Myt;
    typedef _Vector_val
<_Ty, _Ax> _Mybase;
    .......

  這樣,我們就完成了vector對我們Foo結構的特例化,接下來找到剛才產生問題的resize()方法,改為:

void resize(size_type _Newsize, const _Ty & _Val)
    { 
// determine new length, padding with _Val elements as neededif (size() < _Newsize)
            _Insert_n(end(), _Newsize 
- size(), _Val);
        
elseif (_Newsize < size())
            erase(begin() 
+ _Newsize, end());
    }

  注意其中紅色部分,將傳值改為傳參,避免在引數棧上建立被對齊的結構的物件。然後,在我們使用std::vector< Foo<5, float> >之前包含我們的foo_vector.hpp標頭檔案,就可以正常使用了。