不幸的是,确保最大对齐比它应该的要困难得多,并且没有保证的解决方案 AFAIK。来自GotW博客(Fast Pimpl 文章):
union max_align {
short dummy0;
long dummy1;
double dummy2;
long double dummy3;
void* dummy4;
/*...and pointers to functions, pointers to
member functions, pointers to member data,
pointers to classes, eye of newt, ...*/
};
union {
max_align m;
char x_[sizeofx];
};
这不能保证是完全可移植的,但实际上它已经足够接近了,因为很少或没有系统不能按预期工作。
那是我所知道的最接近的“黑客”。
还有另一种我个人用于超快速分配的方法。请注意,这是邪恶的,但我在光线追踪领域工作,其中速度是衡量质量的最大指标之一,我们每天都会分析代码。它涉及使用具有预分配内存的堆分配器,其工作方式类似于本地堆栈(只是在分配时增加一个指针,在解除分配时减少一个)。
我特别将它用于Pimpls。然而,仅仅拥有分配器是不够的;为了让这样的分配器工作,我们必须假设类 Foo 的内存是在构造函数中分配的,同样的内存同样只在析构函数中被释放,并且 Foo 本身是在堆栈上创建的。为了安全起见,我需要一个函数来查看类的“this”指针是否在本地堆栈上,以确定我们是否可以使用我们的超快速基于堆的堆栈分配器。为此,我们必须研究特定于操作系统的解决方案:我将TIB和TEB用于 Win32/Win64,我的同事找到了适用于 Linux 和 Mac OS X 的解决方案。
经过一周研究特定于操作系统的方法来检测堆栈范围、对齐要求并进行大量测试和分析后,结果是一个分配器可以根据我们的滴答计数器基准在 4 个时钟周期内分配内存,而不是大约malloc/operator new 需要 400 个周期(我们的测试涉及线程争用,因此在单线程情况下 malloc 可能会比这快一点,可能是几百个周期)。我们添加了每个线程的堆堆栈并检测到正在使用哪个线程,这将时间增加到大约 12 个周期,尽管客户端可以跟踪线程分配器以获得 4 个周期的分配。它从地图上清除了基于内存分配的热点。
虽然您不必经历所有这些麻烦,但编写一个快速分配器可能比max_align
这里更容易并且更普遍适用(例如:允许在运行时确定分配/释放的内存量)。max_align
很容易使用,但是如果你追求内存分配的速度(假设你已经分析了你的代码并在 malloc/free/operator new/delete 中找到了热点,而主要贡献者在你可以控制的代码中),编写自己的分配器确实可以有所作为。