【问题标题】:How to use STL-compliant allocators for heterogeneous memory allocations如何使用符合 STL 的分配器进行异构内存分配
【发布时间】:2014-09-26 10:30:38
【问题描述】:

我正在尝试实现一个类,该类在内存中后面跟着一个任意类型的数组:

template<class T>
class Buf
{
    size_t n;
    int refs;
    explicit Buf(size_t n) : n(n) { }
    // other declarations are here as appropriate

    // Followed in memory by:
    // T items[n];
};

使用operator new 会很容易:

template<class T>
Buf<T> *make_buf(size_t n)
{
    // Assume the caller will take care of constructing the array elements
    return new(operator new(sizeof(Buf<T>) + sizeof(T) * n)) Buf<T>(n);
}

template<class T>
void free_buf(Buf<T> *p)
{
    // Assume the caller has taken care of destroying the array elements
    p->~Buf<T>();
    return operator delete(p);
}

template<class T>
T *get_buf_array(Buf<T> *p)
{
    return reinterpret_cast<T *>(reinterpret_cast<char *>(p) + sizeof(Buf<T>));
}

但是现在,我如何使用一些符合标准的 allocator SomeAllocator 来实现这一点?

是否保证SomeAllocator::rebind&lt;char&gt;::other::allocate 将返回适合任何类型对象对齐的内存?如果是这样,我是否可以安全地使用某种 char 类型的分配器?如果没有,我是否有其他选择,或者一般分配器不可能完成这项任务? (在最坏的情况下,我想我可以将指针转换为 uintptr_t 并手动对齐它们,但我想知道是否有更好的方法。)

【问题讨论】:

  • 您可以随时请求更多内存,然后使用std::align...
  • @KerrekSB:哇,太好了。我不知道该功能存在...
  • @KerrekSB:我想问题仍然存在:最好的方法是什么,是否有必要调用该函数?我应该使用 char 的分配器还是 T 的分配器?等
  • 它必须是char 的分配器,因为您实际上没有任何可以分配的类型 - 您只需要原始内存。 (不过,它应该是std::allocator_traits&lt;Alloc&gt;::rebind_alloc&lt;char&gt;,而特征应该是rebind_traits......你需要通过特征调用allocate并获得一个本机指针等等。)
  • Buf&lt;T&gt; 的分配器以该类型的倍数分配,所以我认为这没有用。当然,您可以进行一些大小计算,但至少您仍然需要以某种方式对齐第一个数组元素的地址。

标签: c++ memory-management allocator heterogeneous


【解决方案1】:

我认为解决方案可能是创建一个概念上的早期数组。

+-----------+
|Buf<T>     |
+-------+---+---+-------+-------+
|T[0]   | T[1]  |T[2]   |  T[3]...
+-------+-------+-------+-------+

With the non-overlapping T[2], T[3], ... being the required array of T.

template<class T>
class Buf
{
    size_t n;
    int refs;
    explicit Buf(size_t n) : n(n) { }
    // other declarations are here as appropriate

    // Followed in memory by:
    // T items[n];
};

被破坏的元素的数量是:-

const size_t lead = ( sizeof(Buf<T>) + sizeof(T) - 1) / sizeof(T);

终于可以访问i的原始内存了

(reinterpret_cast<T*>( this ) )[ i + lead ];

【讨论】:

    【解决方案2】:

    恐怕您对 C++ 标准的要求做出了毫无根据的假设。您尝试做的事情通常不可能。

    默认分配器(new 或 malloc)需要返回一个指针,该指针指向与any complete object type with a fundamental alignment requirement 适当对齐的内存块。大小必须为at least as large as the requested size。自定义分配器有不同的要求,具体取决于它们分配的内容。一种类型的分配器不能保证返回适合另一种类型的存储。当然,如果您是实现自定义分配器的人,您可以确保它返回您需要的内容。

    编译器需要满足一些关于内存布局的约束,但它不能保证某个东西在其他东西之后立即放置在内存中。可能会插入填充字节以满足对齐要求。

    最近的 C++ 标准为处理对齐提供了相当多的支持。那里的某个地方可能有一个答案。我怀疑这是你没有告诉我们的一些要求。也许还有其他方法可以解决。

    【讨论】:

      【解决方案3】:

      我认为您符合标准的分配器是否符合标准?由于您不需要将分配器与 stl 数据结构一起使用,因此实际上没有必要满足它的要求,即使您也可以这样做,因为我认为这是一种巧妙的方法,在这种情况下您可以通过使用带有自定义 stl 样式分配器的 std::vector 作为模板参数来实现缓冲区。关于 operator new 和 operator new[] 的对齐保证,我建议你看看:

      Is there any guarantee of alignment of address return by C++'s new operation?.

      如果您的对齐问题是针对基本类型(例如双精度数等),那么您几乎可以使用 std::align ,正如您在 http://en.cppreference.com/w/cpp/memory/align 中看到的那样。

      但是,如果您有奇怪/更大的对齐要求,例如将每个元素对齐到缓存行等,或者 T 是一个大小为 sizeof(T)mod 对齐!= 0 的类型,则在分配 T 的数组时可能会遇到问题。在这种情况下,即使数组的第一个元素对齐以满足要求,但这并不意味着所有后续元素也将对齐。

      【讨论】:

        【解决方案4】:

        通过将分配器重新绑定到 std::aligned_storage 的特化来限制对齐。

        typedef std::aligned_storage_t< 1, std::max( alignof (Buf<T>), alignof (T) ) >
                unit_type; // lowest-common-denominator type for aligned allocation
        
        std::size_t unit_count // number of unit_type array elements needed
            = ( sizeof (Buf<T>) + sizeof (T) * n // = actual used storage
                   + sizeof (unit_type) - 1 )    //   + alignment padding
              / sizeof (unit_type);              // divided by element size
        
        typedef typename std::allocator_traits< SomeAllocator >::rebind_alloc< unit_type >
                rebound;
        
        rebound a( alloc_parm ); // Retain this somewhere if statefulness is allowed.
        ptr = std::allocator_traits< rebound >::allocate( a, unit_count );
        

        (记住,所有分配器访问都通过allocator_traits!)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-06-07
          • 2016-04-10
          • 2015-07-04
          • 1970-01-01
          • 2012-09-18
          • 2014-12-07
          • 1970-01-01
          相关资源
          最近更新 更多