分配对齐结构的数组

Allocating an array of aligned struct

本文关键字:数组 结构 对齐 分配      更新时间:2023-10-16

>我正在尝试分配一个结构数组,我希望每个结构都对齐到 64 个字节。

试过这个(目前仅适用于 Windows),但它不起作用(我尝试使用 VS2012 和 VS2013):

struct __declspec(align(64)) A
{
    std::vector<int> v;
    A()
    {
        assert(sizeof(A) == 64);
        assert((size_t)this % 64 == 0);
    }
    void* operator new[] (size_t size)
    {
        void* ptr = _aligned_malloc(size, 64); 
        assert((size_t)ptr % 64 == 0);
        return ptr;
    }
    void  operator delete[] (void* p)
    {
        _aligned_free(p);
    }
};
int main(int argc, char* argv[])
{
    A* arr = new A[200];
    return 0;
}

断言((size_t)this % 64 == 0)中断(模数返回 16)。如果结构仅包含简单类型,它看起来可以工作,但是当它包含std容器(或其他一些 std 类)时会中断。

我做错了什么吗?有没有办法正确地做到这一点?(最好兼容 c++03,但任何适用于 VS2012 的解决方案都可以)。

编辑:正如Shokwav所暗示的那样,这是有效的:

A* arr = (A*)new std::aligned_storage<sizeof(A), 64>::type[200];
// this works too actually:
//A* arr = (A*)_aligned_malloc(sizeof(A) * 200, 64);
for (int i=0; i<200; ++i)
    new (&arr[i]) A();

所以看起来它与使用new[]有关...我很好奇是否有人有解释。

我想知道为什么你需要如此巨大的对齐要求,而且要在结构中存储动态堆分配的对象。但你可以这样做:

struct __declspec(align(64)) A
{
    unsigned char ___padding[64 - sizeof(std::vector<int>)];
    std::vector<int> v;
    void* operator new[] (size_t size)
    {
        // Make sure the buffer will fit even in the worst case
        unsigned char* ptr = (unsigned char*)malloc(size + 63);
        // Find out the next aligned position in the buffer
        unsigned char* endptr = (unsigned char*)(((intptr_t)ptr + 63) & ~63ULL);
        // Also store the misalignment in the first padding of the structure 
        unsigned char misalign = (unsigned char)(endptr - ptr);
        *endptr = misalign;
        return endptr;
    }
    void  operator delete[] (void* p)
    {
        unsigned char * ptr = (unsigned char*)p;
        // It's required to call back with the original pointer, so subtract the misalignment offset
        ptr -= *ptr;
        free(ptr);
    }
};
int main()
{
    A * a = new A[2];
    printf("%p - %p = %dn", &a[1], &a[0], int((char*)&a[1] - (char*)&a[0]));
    return 0;
}

我没有您的align_malloc和免费功能,所以我提供的实现是这样做的:

  1. 分配更大的内容以确保它适合 64 字节的边界
  2. 它计算从分配到最接近的 64 字节边界的偏移
  3. 它将"偏移量"存储在第一个结构的填充中(否则我每次都需要更大的分配空间)
  4. 这用于计算回指向 free() 的原始指针

输出:

0x7fff57b1ca40 - 0x7fff57b1ca00 = 64

警告:如果您的结构中没有填充,那么上面的方案将损坏数据,因为我将未对齐偏移量存储在将被内部成员的构造函数覆盖的位置。请记住,当你执行"new X[n]"时,"n"必须存储在"某处",因此在调用delete[]时,将对析构函数进行"n"调用。通常,它存储在返回的内存缓冲区之前(new 可能会分配所需的大小 + 4 来存储元素的数量)。这里的方案避免了这一点。

另一个警告:由于C++调用此运算符时,大小中包含一些额外的填充,用于存储数组的元素数,因此您可能仍会在对象的返回指针地址中获得"shift"。您可能需要考虑它。这就是 std::align 所做的,它占用额外的空间,像我一样计算对齐并返回对齐的指针。但是,您无法在 new[] 重载中同时完成这两项工作,因为从 new() 返回会发生"计数存储"偏移。但是,您可以通过单个分配计算一次"计数存储"空间,并在 new[] 实现中相应地调整偏移量。