gpt4 book ai didi

c++ - 防止堆上的未对齐数据

转载 作者:可可西里 更新时间:2023-11-01 16:40:07 26 4
gpt4 key购买 nike

我正在构建一个使用 SSE 内部函数的类层次结构,因此该类的一些成员需要 16 字节对齐。对于堆栈实例,我可以使用 __declspec(align(#)),如下所示:

typedef __declspec(align(16)) float Vector[4];
class MyClass{
...
private:
Vector v;
};

现在,由于 __declspec(align(#)) 是一个编译指令,以下代码可能会导致堆上的 Vector 实例未对齐:

MyClass *myclass = new MyClass;

这也是,我知道我可以通过重载 newdelete 运算符以使用 _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);
}
...

到目前为止一切顺利..但这是我的问题。考虑以下代码:

class NotMyClass{ //Not my code, which I have little or no influence over
...
MyClass myclass;
...
};
int main(){
...
NotMyClass *nmc = new NotMyClass;
...
}

由于 MyClass 的 myclass 实例是在 NotMyClass 的动态实例上静态创建的,由于 Vector 的 __declspec(align(16),myclass 将相对于 nmc 的开头进行 16 字节对齐)) 指令。但这是毫无值(value)的,因为 nmc 是使用 NotMyClass 的 new 运算符在堆上动态分配的,这不一定确保(而且绝对可能不会)16 字节对齐。

到目前为止,我只能想到两种方法来处理这个问题:

  1. 防止 MyClass 用户能够编译以下代码:

    MyClass myclass;

    意思是,MyClass 的实例只能使用 new 运算符动态创建,从而确保 MyClass 的所有实例都真正动态分配了 MyClass 的重载 new。我在另一个线程上咨询了如何实现这一点,并得到了一些很好的答案: C++, preventing class instance from being created on the stack (during compiltaion)

  2. 不再在我的类中使用 Vector 成员,只将指向 Vector 的指针作为成员,我将在 ctor 中使用 _aligned_malloc_aligned_free 分配和解除分配和dtor分别。这种方法看起来粗糙且容易出错,因为我不是唯一编写这些类的程序员(MyClass 派生自基类,其中许多类使用 SSE)。

但是,由于我的团队不赞成这两种解决方案,因此我向您寻求不同解决方案的建议。

最佳答案

如果您反对堆分配,另一个想法是在堆栈上过度分配并手动对齐(手动对齐在 this SO post 中讨论)。这个想法是分配字节数据(unsigned char),其大小保证包含必要大小(+15)的对齐区域,然后通过舍入找到对齐位置从移动最多的区域向下(x+15 - (x+15) % 16,或 x+15 & ~0x0F)。我在 codepad 上发布了一个使用 vector 运算的这种方法的工作示例(对于 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
) )
{}
...
};
...

构造函数确保 vPtr 对齐(注意类声明中成员的顺序很重要)。

这种方法有效(包含类的堆/堆栈分配与对齐无关),是可移植的(我认为大多数编译器提供一个指针大小的 uint uintptr_t),并且不会泄漏内存。但它不是特别安全(确保在拷贝下保持对齐的指针有效等),浪费(几乎)与其使用的内存一样多,有些人可能会觉得 reinterpret_casts 令人反感。

通过将此逻辑封装在 Vector 对象中,可以大部分消除对齐操作/未对齐数据问题的风险,从而控制对对齐指针的访问并确保它在构造时对齐并保持有效。

关于c++ - 防止堆上的未对齐数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3095856/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com