【问题标题】:operator new overloading and alignmentoperator new 重载和对齐
【发布时间】:2011-01-22 22:31:31
【问题描述】:

我正在重载operator new,但我最近遇到了对齐问题。基本上,我有一个 IBase 类,它在所有必需的变体中提供 operator newdelete。所有类都派生自 IBase,因此也使用自定义分配器。

我现在面临的问题是我有一个孩子Foo,它必须是 16 字节对齐的,而其他所有的在对齐到 8 字节时都很好。然而,我的内存分配器默认仅与 8 字节边界对齐,所以现在IBase::operator new 中的代码返回一块不可用的内存。这应该如何正确解决?

我可以简单地将所有分配强制为 16 字节,这将正常工作,直到弹出 32 字节对齐类型。弄清楚operator new 内部的对齐方式似乎并不简单(我可以在那里调用虚函数来获得实际的对齐方式吗?)推荐的处理方法是什么?

我知道malloc 应该返回一块适合所有内容对齐的内存,不幸的是,这个“所有内容”不包括 SSE 类型,我真的很想在不需要用户的情况下让它工作记住哪种类型有哪种对齐方式。

【问题讨论】:

  • 也许您可以为少数有特殊对齐要求的对象使用不同的 IBase (IBase16?)。
  • 您甚至可以将此基类(IBase16、IBase32)模板化,这样您就可以使用 IBase
  • 您可以在 64 字节边界上对齐。 :)
  • jalf,IBase 只是一个占位符名称。这根本不会改变手头的问题。

标签: c++ operator-overloading new-operator


【解决方案1】:

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

#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&lt;I&gt; 继承DeAllocator&lt;I / 2&gt; 的问题。答案是因为在给定的层次结构中,类可能有不同的对齐要求。假设IBase没有对齐要求,A有8字节要求,B有16字节要求并继承A

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

Alignment&lt;16&gt;Alignment&lt;8&gt; 都公开了一个 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&lt;8&gt; 虚拟继承Alignment&lt;16&gt; 并使AB 虚拟继承它们,则Alignment&lt;8&gt; 中的operator new 将被隐藏:

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

这种特殊的隐藏规则(也称为 dominance 规则)仅在 all Alignment&lt;8&gt; 对象相同时才有效。因此,我们总是虚拟继承:在这种情况下,任何给定的类层次结构中都只有 一个 Alignment&lt;8&gt;(或 16 个,...)对象。

【讨论】:

  • 很好地使用了模板,我喜欢你的想法,但我一定遗漏了一些明显的东西,因为我看不到对齐是如何完成的。假设将使用 OP 使用的任何编译器提供的技术打包结构?
  • @John,在此示例中,对齐是通过打印其值来指示的。您可以将整数传递给posix_memalign 或其他东西。我怀疑@Anteru 已经想办法做到这一点了。
  • 我看不到这里的对齐方式。可能你在这里并没有真正实现对齐(只是展示了如何使用模板来指示可能的对齐大小),但在这种情况下,你不会用普通的模板类获得相同的结果(不是从另一个模板类继承),其中 Align16 继承自 DeAllocator 而 Align8 继承自 DeAllocator (并且没有多重继承)?
  • @Patrick,那么问题是有两个operator new 函数,你有一个歧义(当然,我假设Align16 继承Align8,就像Foo 将继承IBase)。来自Foo::DeAllocator&lt;16&gt; 或来自IBase::DeAllocator&lt;8&gt; 的那个?
  • @litb:此解决方案是否仅在可以在运行时指定对齐时才有效?我不知道在 Windows 下运行时指定对齐的方法;仅在编译时。
【解决方案2】:

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)));

请注意,最大对齐方式会自动选择,这适用于放置在堆栈上的对象、新对象以及其他类的成员。它也没有添加任何虚拟变量并假设为 EBCO,也没有给类增加额外的大小(在对齐本身所需的填充之外)。

【讨论】:

    猜你喜欢
    • 2019-04-10
    • 2021-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多