ela*_*dan 9 c++ alignment visual-c++
我正在构建一个使用SSE内在函数的类层次结构,因此该类的一些成员需要16字节对齐.对于我可以使用的堆栈实例__declspec(align(#)),如下所示:
typedef __declspec(align(16)) float Vector[4];
class MyClass{
...
private:
Vector v;
};
Run Code Online (Sandbox Code Playgroud)
现在,因为__declspec(align(#))是一个编译指令,下面的代码可能会导致堆上的未对齐的Vector实例:
MyClass *myclass = new MyClass;
Run Code Online (Sandbox Code Playgroud)
这也是,我知道我可以通过重载新的和删除操作符来轻松解决使用_aligned_malloc和_aligned_free相应的问题.像这样:
//inside MyClass:
public:
void* operator new (size_t size) throw (std::bad_alloc){
void * p = _aligned_malloc(size, 16);
if (p == 0) throw std::bad_alloc()
return p;
}
void operator delete (void *p){
MyClass* pc = static_cast<MyClass*>(p);
_aligned_free(p);
}
...
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都很好..但这是我的问题.请考虑以下代码:
class NotMyClass{ //Not my code, which I have little or no influence over
...
MyClass myclass;
...
};
int main(){
...
NotMyClass *nmc = new NotMyClass;
...
}
Run Code Online (Sandbox Code Playgroud)
由于myclass实例MyClass是在NotMyClass的动态实例上静态创建的,因为Vector的__declspec(align(16))指令,myclass将相对于nmc的开头16字节对齐.但这是毫无价值的,因为nmc是使用NotMyClass的new运算符在堆上动态分配的,它不会确保(并且绝对可能不是)16字节对齐.
到目前为止,我只能想到如何处理这个问题的两种方法:
阻止MyClass用户编译以下代码:
MyClass myclass;
Run Code Online (Sandbox Code Playgroud)
意思是,MyClass的实例只能使用new运算符动态创建,从而确保MyClass的所有实例都是真正动态分配MyClass的重载新实例.我已经咨询了另一个关于如何实现这一点的线程,并得到了一些很好的答案: C++,防止在堆栈上创建类实例(在编译期间)
从我的类中的Vector成员恢复,并且只有指向Vector作为成员的指针,我将分别使用_aligned_malloc和分配_aligned_freector和dtor 来分配和解除分配.这个方法似乎很粗糙,容易出错,因为我不是编写这些类的唯一程序员(MyClass派生自Base类,其中许多类使用SSE).
但是,由于我的团队对这两种解决方案都不满意,因此我会向您寻求不同解决方案的建议.
如果您反对堆分配,另一个想法是在堆栈上过度分配并手动对齐(在此 SO post中讨论了手动对齐)。这个想法是分配字节数据 ( unsigned char) ,其大小保证包含必要大小的对齐区域 ( +15),然后通过从最移位区域 ( x+15 - (x+15) % 16、 或x+15 & ~0x0F) 向下舍入来找到对齐位置。我在键盘上发布了此方法的矢量操作示例(适用于g++ -O2 -msse2)。以下是重要的部分:
class MyClass{
...
unsigned char dPtr[sizeof(float)*4+15]; //over-allocated data
float* vPtr; //float ptr to be aligned
public:
MyClass(void) :
vPtr( reinterpret_cast<float*>(
(reinterpret_cast<uintptr_t>(dPtr)+15) & ~ 0x0F
) )
{}
...
};
...
Run Code Online (Sandbox Code Playgroud)
构造函数确保 vPtr 对齐(注意类声明中成员的顺序很重要)。
这种方法有效(包含类的堆/堆栈分配与对齐无关),是可移植的(我认为大多数编译器提供指针大小的 uint uintptr_t),并且不会泄漏内存。但它并不是特别安全(确保在复制等情况下保持对齐指针有效),浪费(几乎)与它使用的一样多的内存,并且有些人可能会觉得reinterpret_casts令人厌恶。
通过将此逻辑封装在 Vector 对象中,可以基本上消除对齐操作/未对齐数据问题的风险,从而控制对对齐指针的访问并确保它在构造时对齐并保持有效。
| 归档时间: |
|
| 查看次数: |
3915 次 |
| 最近记录: |