代码之家  ›  专栏  ›  技术社区  ›  Skurmedel

在设计内存池时,在对齐方面应该考虑什么?

  •  7
  • Skurmedel  · 技术社区  · 14 年前

    我正在为一个小游戏引擎开发一个内存池。

    主要用途是作为独立的存储;池包含特定类型和大小的对象。目前,池可以用来存储任何东西,但分配将在特定大小的块中完成。所需的大部分内存将立即分配,但如果需要帮助调优(几乎固定大小),可以启用“过度增长”。

    当前的方法是分配一块内存 blocks * (desired_size + header_size) 将对象放大并放置在其中,每个块都有一个标题;显然,对象将直接放置在这个标题后面。

    desired_size n -字节对齐的数据;编译器正确地对齐和打包了头文件以及实际数据,块中存储的所有内容都将 n

    n是平台要求的任何边界。目前我的目标是x86,但我不想在代码中对平台做任何假设。

    我使用过的一些资源:

    编辑

    上传的小样本代码,可能有助于任何人一样困惑我在未来 here .

    5 回复  |  直到 8 年前
        1
  •  8
  •   Steve Jessop    14 年前

    分配 malloc 保证与编译器提供的任何类型对齐,从而与任何对象[*]对齐。

    危险是当您的头的对齐要求小于实现的最大对齐要求时。那么它的大小可能不是最大对齐的倍数,所以当您尝试转换/使用 buf + header_size 最大对齐,不对中。就C而言,这是未定义的行为。在英特尔上,它可以工作,但速度较慢。在某些ARM上,它会导致硬件异常。在某些手臂上,它默默地给出了错误的答案。因此,如果您不想在代码中对平台进行假设,就必须处理它。

    基本上,我知道有三个技巧可以确保标头不会导致未对齐:

    • 使用特定于平台的结构布局和对齐知识,确保其大小恰好是系统上最大对齐要求的倍数。通常这意味着,“多加一个 int 如有必要,可将其作为填充,使其成为8倍,而不仅仅是4倍”。
    • 使标题成为 union

    或者,您可以定义 header_size 不可能 sizeof(header) ,但是把这个尺寸四舍五入到2的倍数,这就足够了。如果你浪费了一点内存,那就这样吧,你总是可以有一个“可移植性头”来定义这类东西,它的定义方式不是完全独立于平台,而是让你很容易适应新的平台。

    [*]一个常见的例外是SIMD类型过大。因为它们是非标准的,而且仅仅因为它们就对齐每个分配是浪费的,所以它们会被放在一边,您需要为它们提供特殊的分配函数。

        2
  •  2
  •   Hans Passant    14 年前

    当对象跨越CPU缓存线边界时,错误对齐对象可能会非常昂贵。跨两条缓存线的double的读写时间是double的三倍。

        3
  •  2
  •   Matthieu M.    14 年前

    有两种类型的工具来帮助编写正确的内存分配器(C++ 0x),它们可以在 std::tr1 在大多数STL中):

    • std::alignment_of
    • std::aligned_storage

    和他们两个一起工作,你会得到你需要的。

    但是,您的设计稍微偏离了基准,这并不是很重要,因为您的标题与存储的对象没有相同的对齐要求(通常)。

    template <class T, size_t N>
    struct Block
    {
      // Header bits
    
      typedef std::aligned_storage<
          N*sizeof(T),
          std::alignment_of<T>::value
      >::type buffer_type;
      buffer_type mBuffer;
    };
    

    • Block 它本身可能有不同的排列方式,但这并不重要
    • sizeof 对于对齐,它保证工作,即使有点浪费

    最后,你也可以成功,没有所有这些,一些指针算法,但因为它提供。。。。

        5
  •  1
  •   f0b0s    14 年前

    据我所知,boost::pool是基于alexandrescu在《现代c++设计》一书中解释的“小对象分配器”。我必须读这本书(因为你写这些东西也是为了学习)