分配对齐结构的数组

时间:2015-06-19 15:16:26

标签: c++ memory-alignment

我正在尝试分配一个struct数组,我希望每个结构都与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)。如果struct只包含简单类型,它看起来有效,但是当它包含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 []有关...如果有人有解释我很好奇。

1 个答案:

答案 0 :(得分:0)

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

<add name="FactsAndFiguresEntities1"        connectionString="metadata=res://*/Models.Model1.csdl|res://*/Models.Model1.ssdl|res://*/Models.Model1.msl;provider=System.Data.SqlClient;provider connection string='data source=SERVERNAME User Id=****; Password = **** catalog=FactsAndFigures;integrated security=True;multipleactiveresultsets=True;application name=EntityFramework'" providerName="System.Data.EntityClient" />

我没有你的align_malloc和free函数,所以我提供的实现是这样做的:

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

    输出:

    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 = %d\n", &a[1], &a[0], int((char*)&a[1] - (char*)&a[0]));
        return 0;
    }
    

    警告:如果您的结构中没有填充,那么上面的方案将损坏数据,因为我将错误的偏移存储在一个被覆盖的地方由内部成员的构造函数。 请记住,当你做&#34;新X [n]&#34;,&#34; n&#34;必须存储&#34;某处&#34;所以当调用delete []时,&#34; n&#34;对析构函数的调用将完成。通常,它存储在返回的内存缓冲区之前(new可能会分配所需的大小+4来存储元素的数量)。这个计划避免了这个。

    另一个警告:因为C ++调用此运算符并在大小中包含一些额外的填充来存储数组的元素数量,所以你可能仍会得到一个&#34 ;移&#34;在返回的对象的指针地址中。您可能需要考虑它。这就是std :: align所做的,它需要额外的空间,像我一样计算对齐并返回对齐的指针。但是,由于&#34;计数存储&#34;你不能在new []重载中完成两者。从new()返回之后发生的转移。但是,你可以找出&#34;计数存储&#34;通过单个分配对空间进行一次,并在新[]实现中相应地调整偏移量。