自定义堆栈分配器中的C ++内存对齐

时间:2011-08-29 23:01:50

标签: c++ memory-management

通常,数据根据其大小以两个地址的幂对齐。

我应该如何对齐大小为20字节的结构或类或另一种非二次幂的大小?

我正在创建一个自定义堆栈分配器,所以我猜编译器不会为我对齐数据,因为我正在使用连续的内存块。

更多背景信息:

我有一个使用malloc()分配大量数据的Allocator类。 然后我使用void * allocate(U32 size_of_object)方法返回指针,我可以在哪里存储我需要存储的对象。 这样,所有对象都存储在相同的内存区域中,并且它有望适合缓存,从而减少缓存未命中。

6 个答案:

答案 0 :(得分:3)

虽然编译器(或解释器)通常在对齐的边界上分配单个数据项,但数据结构通常具有不同对齐要求的成员。为了保持正确的对齐,翻译器通常会插入其他未命名的数据成员,以便每个成员都正确对齐。此外,整个数据结构可以用最终未命名的成员填充。这允许结构阵列的每个成员正确对齐。 http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86

这表示编译器会为您处理它,99.9%的时间。至于如何强制对象以特定方式对齐特定方式,并且仅在某些情况下有效。

MSVC:http://msdn.microsoft.com/en-us/library/83ythb65.aspx

__declspec(align(20)) 
struct S{ int a, b, c, d; };
//must be less than or equal to 20 bytes

海湾合作委员会:http://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Type-Attributes.html

struct S{ int a, b, c, d; } 
__attribute__ ((aligned (20)));

我不知道这样做的跨平台方式(包括宏!),但在某处可能有整齐的宏。

答案 1 :(得分:3)

C ++ 11具有专门用于此目的的alignof运算符。不要使用其他帖子中提到的任何技巧,因为它们都有边缘情况,或者可能因某些编译器优化而失败。 alignof运算符由编译器实现,并且知道正在使用的对齐方式。

See this description of c++11's new alignof operator

答案 2 :(得分:1)

除非您想直接访问内存,或者在内存块中挤压最大数据,否则您不必担心对齐 - 编译器会为您解决这个问题。

答案 3 :(得分:1)

由于处理器数据总线的工作方式,您要避免的是“错位”访问。通常,您可以从一次访问中读取32位值,该地址是四的倍数;如果你试图从一个不是这么多的地址读取它,CPU可能必须抓住两个或多个部分。所以,如果你真的在这个细节层面上担心事情,那么你需要关注的不是整体结构,而是整体结构。您会发现编译器会经常使用虚拟字节填充结构以确保对齐访问,除非您特别强制它们不使用编译指示。

答案 4 :(得分:1)

由于您现在已经添加了实际上想要编写自己的分配器,因此答案很简单:只需确保分配器返回一个指针值,该指针的值是请求大小的倍数。对象的大小本身已经适当调整(通过内部填充),以便所有成员对象本身都正确对齐,所以如果你请求sizeof(T)个字节,你的所有分配器需要做的就是返回一个值可分的指针sizeof(T)

如果您的对象确实具有大小为20(由sizeof报告),那么您无需进一步担心。 (在64位平台上,对象可能会填充为24个字节。)

更新:事实上,正如我现在才意识到的那样,严格来说,您只需要确保指针以递归方式对齐,以便最大的成员你的类型。这可能更有效,但是与整个类型的大小保持一致绝对不会出错。

答案 5 :(得分:0)

  

我应该如何对齐大小为20字节的结构或类或另一种非二次幂的大小?

对齐是特定于CPU的,因此至少在不了解目标CPU的情况下,对此问题没有答案。

一般来说,对齐不是你必须担心的事情;您的编译器将为您实现规则。它偶尔出现,就像编写分配器一样。在 C编程语言(K& R)中讨论了经典的解决方案:使用最差的对齐方式。 malloc执行此操作,尽管it's phrased as,“如果分配成功,则返回的指针应适当对齐,以便可以将其指定给指向任何类型对象的指针。”

的方法是使用unionunion的元素都在union的基地址分配,并且因此,union必须以这样一种方式对齐,即每个元素都可以存在于该地址;即union的对齐方式与元素与最严格规则的对齐方式相同):

typedef Align long;
union header {
    // the inner struct has the important bookeeping info
    struct {
        unsigned size;
        header* next; 
    } s;
    // the align member only exists to make sure header_t's are always allocated
    // using the alignment of a long, which is probably the worst alignment
    // for the target architecture ("worst" == "strictest," something that meets
    // the worst alignment will also meet all better alignment requirements)
    Align align;
};

通过创建一个大小足以满足请求的sbrk()的数组(使用像header这样的东西)来分配内存,另外还有一个实际包含簿记信息的header元素。如果数组被调用arry,则簿记信息位于arry[0],而指针返回arry[1]处的点(next元素用于遍历空闲列表)。

This works, but can lead to wasted space(“在Sun的HotSpot JVM中,对象存储与最近的64位边界对齐”)。我知道a better approach试图获得特定于类型的对齐,而不是“对任何东西都有效的对齐。”

编译器通常还具有特定于编译器的命令。它们不是标准的,它们要求您知道相关类型的正确对齐要求。我会避免他们。