重载operator new和对齐方式

36
我正在重载operator new,但最近遇到了对齐的问题。基本上,我有一个类IBase,它提供了所需的所有变体的operator newdelete。所有类都是从IBase派生而来,因此也使用自定义分配器。
现在我面临的问题是,我有一个子类Foo,它必须16字节对齐,而其他所有类在对齐到8字节时都可以正常工作。然而,我的内存分配器默认仅对齐到8字节边界,因此IBase::operator new中的代码返回了一个无法使用的内存块。这应该如何正确地解决?
我可以简单地强制将所有分配都设置为16字节,这样可以正常工作,直到出现32字节对齐的类型。在operator new中找到对齐方式似乎并不容易(我能在那里做一个虚函数调用来获取实际的对齐方式吗?)。处理这个问题的推荐方法是什么?
我知道malloc应该返回适合一切的内存块,但不幸的是,“一切”不包括SSE类型,我真的想让这个工作正常,而不需要用户记住哪个类型具有哪种对齐方式。

也许您可以为那些具有特殊对齐要求的少数对象使用不同的IBase(例如IBase16)。 - John Knoeller
你甚至可以将这个基类(IBase16、IBase32)模板化,这样你就可以使用 IBase<alignmentsize>。 - Patrick
你可以对齐到64字节边界。 :) - Bill
jalf,IBase只是一个占位符名称。这并不改变手头的问题。 - Anteru
2个回答

21

这是一种可能的解决方案。它将始终选择给定层次结构中对齐最高的运算符:

#include <exception>
#include <iostream>
#include <cstdlib>

// provides operators for any alignment >= 4 bytes
template<int Alignment>
struct DeAllocator;

template<int Alignment>
struct DeAllocator : virtual DeAllocator<Alignment/2> {
  void *operator new(size_t s) throw (std::bad_alloc) {
    std::cerr << "alignment: " << Alignment << "\n";
    return ::operator new(s);
  }

  void operator delete(void *p) {
    ::operator delete(p);
  }
};

template<>
struct DeAllocator<2> { };

// ........... Test .............
// different classes needing different alignments
struct Align8 : virtual DeAllocator<8> { };
struct Align16 : Align8, virtual DeAllocator<16> { };
struct DontCare : Align16, virtual DeAllocator<4> { };

int main() {
  delete new Align8;   // alignment: 8
  delete new Align16;  // alignment: 16
  delete new DontCare; // alignment: 16
}

基于支配规则:如果查找时存在歧义,并且歧义在派生类和虚基类的名称之间,则使用派生类的名称。


有人问为什么 DeAllocator<I> 继承 DeAllocator<I / 2>。答案是因为在给定的继承层次结构中,不同的类可能会强制实施不同的对齐要求。假设 IBase 没有对齐要求,A 需要8字节对齐,B 需要16字节对齐并继承 A:

class IBAse { };
class A : IBase, Alignment<8> { };
class B : A, Alignment<16> { };

Alignment<16>Alignment<8>都公开了一个operator new。如果你现在说new B,编译器将在B中查找operator new,并会找到两个函数:

            // op new
            Alignment<8>      IBase
                 ^            /
                  \         /
                    \     /
 // op new            \ /
 Alignment<16>         A
            \         /
              \     /
                \ /
                 B 

B ->      Alignment<16>  -> operator new
B -> A -> Alignment<8> -> operator new

因此,这将是不明确的,我们将无法编译:两者都没有隐藏另一个。但是如果您现在从Alignment<8>虚拟继承Alignment<16>,并使AB虚拟继承它们,则Alignment<8>中的operator new将被隐藏:

            // op new
            Alignment<8>      IBase
                 ^            /
                / \         /
              /     \     /
 // op new  /         \ /
 Alignment<16>         A
            \         /
              \     /
                \ /
                 B 

这个特殊的隐藏规则(也称为支配规则)仅在所有Alignment<8>对象都相同时才有效。因此我们总是以虚拟方式继承: 在这种情况下,任何给定类层次结构中只存在一个Alignment<8>(或16,...)对象。


很好的模板使用,我喜欢你的思路,但是我可能错过了一些显而易见的东西,因为我不知道对齐是如何完成的。假设结构体将使用OP所使用的编译器提供的技术进行打包? - John Dibling
@John,在这个示例中,通过打印其值来指示对齐方式。您将把整数传递给posix_memalign或其他函数。@Anteru可能已经想出了一种方法来做到这一点。 - Johannes Schaub - litb
1
我不明白这里是如何实现对齐的。可能你在这里并没有真正实现对齐(只是展示如何使用模板来指示可能的对齐大小),但如果是这样的话,你不会用一个普通的模板类(不从另一个模板类派生)获得同样的结果吗?在这种情况下,Align16继承自DeAllocator<16>,Align8继承自DeAllocator<8>(而且不进行多重继承)? - Patrick
@Patrick,问题在于有两个operator new函数,因此存在歧义(当然,我假设Align16继承自Align8,就像Foo将继承自IBase一样)。是来自Foo::DeAllocator<16>还是来自IBase::DeAllocator<8> - Johannes Schaub - litb
@Matthieu,以前从未听说过。 - Johannes Schaub - litb
显示剩余5条评论

7
混合(mixins)是正确的方法,但重载operator new操作符不是。这将实现您所需的功能:
__declspec(align(256))  struct cachealign{};
__declspec(align(4096)) struct pagealign{};
struct DefaultAlign{};
struct CacheAlign:private cachealign{};
struct PageAlign: CacheAlign,private pagealign{};

void foo(){
 DefaultAlign d;
 CacheAlign c;
 PageAlign p;
 std::cout<<"Alignment of d "<<__alignof(d)<<std::endl;
 std::cout<<"Alignment of c "<<__alignof(c)<<std::endl;
 std::cout<<"Alignment of p "<<__alignof(p)<<std::endl;
}

打印

Alignment of d 1
Alignment of c 256
Alignment of p 4096

对于gcc,使用

struct cachealign{}__attribute__ ((aligned (256)));

请注意,自动选择最大对齐方式,并且它适用于放置在堆栈上的对象,new的对象以及作为其他类成员的对象。它也不会添加任何虚拟函数,并假定采用EBCO,不会增加类的任何额外大小(除了对齐本身所需的填充)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接