boost::pool<>::malloc 和 boost::pool<>::ordered_malloc 之间有什么区别,什么时候应该使用 boost::pool<>::ordered_malloc?

2024-05-08

我正在使用 boost.pool,但我不知道什么时候使用boost::pool<>::malloc and boost::pool<>::ordered_malloc?

so,

  1. 有什么区别boost::pool<>::malloc and boost::pool<>::ordered_malloc?

  2. 我什么时候应该使用boost::pool<>::ordered_malloc?


First, we should know the basic idea behind the Boost Pool library: simple_segregated_storage, it is similar to a singly linked list, and responsible for partitioning a memory block into fixed-size chunks: enter image description here

A memory pool keeps a free list of memory chunks. So we mentioned blocks and chunks: the memory pool uses new or malloc to allocate a memory block and divides it into many memory chunks which have same size.
Assume the address is aligned by 8, 4 bytes for storing the address of next chunk, so a memory block(8 bytes * 32 chunks) is as below(the memory address is just for illustrating the question, not a real one):
a memory block

现在,假设用户两次分配 8 字节内存,因此使用块:[0xDD00,0xDD08), [0xDD08,0xDD10)。一段时间后,用户释放了[0xDD00,0xDD08)处的内存,所以这个块会回到空闲列表中。现在块是这样的:

enter image description here
Afterwards the user releases the memory at [0xDD08,0xDD10), the simplest way to place this chunk back in the list is to update the first to point to it, constant time complexity. the simple_segregated_storage<T>::free() is doing this exactly:

void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk)
{ //! Free a chunk.
  //! \pre chunk was previously returned from a malloc() referring to the same free list.
  //! \post !empty()
   BOOST_POOL_VALIDATE_INTERNALS
  nextof(chunk) = first;
  first = chunk;
  BOOST_POOL_VALIDATE_INTERNALS
}

After that, the list would be like this:
unordered list
Now we noticed the list of chunks are not ordered by their address after these operations! If we want to preserve the order while de-allocating, call pool<>::ordered_free() instead of pool<>::free() to places the memory back in the list in its proper order. Now we've known what's the order in the memory pool, let's dig into the source code of boost::pool<>::malloc and boost::pool<>::ordered_malloc:

void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
{
  if (!store().empty())
    return (store().malloc)();
  return malloc_need_resize();
}

void * ordered_malloc()
{
  if (!store().empty())
    return (store().malloc)();
  return ordered_malloc_need_resize();
}

正如我们所看到的,只有当内存块列表中没有空闲块时,它们才会有所不同。在这种情况下,它分配一个新的内存块,将其空闲列表合并到池的空闲列表中,这两种方法之间的区别在于boost::pool<>::ordered_malloc合并空闲列表时保留顺序。
以上是针对问题1。
那么,为什么顺序很重要?看来内存池与无序块完美配合!
首先,如果我们想要找到 n 个块的连续序列,有序空闲列表会让这件事变得更容易。其次,我们看一下它的派生类boost::pool: boost::object_pool,它提供了在销毁时自动销毁未释放的对象object_pool对象,同时您也可以手动销毁对象,例如:

class X { … };

    void func()
    {
        boost::object_pool<X> alloc;

        X* obj1 = alloc.construct();
        X* obj2 = alloc.construct();
        alloc.destroy(obj2);
    }

上面的代码没问题,没有内存泄漏,也没有双重删除!如何boost::object_pool做这个魔法吗?我们来看看析构函数的实现boost::object_pool(我的机器上有 boost 1.48):

template <typename T, typename UserAllocator>
object_pool<T, UserAllocator>::~object_pool()
{
#ifndef BOOST_POOL_VALGRIND
  // handle trivial case of invalid list.
  if (!this->list.valid())
    return;

  details::PODptr<size_type> iter = this->list;
  details::PODptr<size_type> next = iter;

  // Start 'freed_iter' at beginning of free list
  void * freed_iter = this->first;

  const size_type partition_size = this->alloc_size();

  do
  {
    // increment next
    next = next.next();

    // delete all contained objects that aren't freed.

    // Iterate 'i' through all chunks in the memory block.
    for (char * i = iter.begin(); i != iter.end(); i += partition_size)
    {
      // If this chunk is free,
      if (i == freed_iter)
      {
        // Increment freed_iter to point to next in free list.
        freed_iter = nextof(freed_iter);

        // Continue searching chunks in the memory block.
        continue;
      }

      // This chunk is not free (allocated), so call its destructor,
      static_cast<T *>(static_cast<void *>(i))->~T();
      // and continue searching chunks in the memory block.
    }

    // free storage.
    (UserAllocator::free)(iter.begin());

    // increment iter.
    iter = next;
  } while (iter.valid());

  // Make the block list empty so that the inherited destructor doesn't try to
  // free it again.
  this->list.invalidate();
#else
   // destruct all used elements:
   for(std::set<void*>::iterator pos = this->used_list.begin(); pos != this->used_list.end(); ++pos)
   {
      static_cast<T*>(*pos)->~T();
   }
   // base class will actually free the memory...
#endif
}

它遍历内存块列表中的所有块(list,数据成员boost::pool<>,保存系统分配的所有内存块的位置和大小),查找其中是否有任何块也在空闲列表中,如果没有,则调用该对象的析构函数,然后释放内存。所以这有点像得到两个集合的交集,就像std::set_intersection() http://www.cplusplus.com/reference/algorithm/set_intersection/做!如果列表已排序,那么这样做会快得多。实际上在boost::object_pool<>,顺序为必填项,公共成员函数:boost::object_pool<>::malloc() and boost::object_pool<>::free()只需致电boost::pool<>::ordered_malloc() and boost::pool<>::ordered_free()分别:

element_type * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
{ //! Allocates memory that can hold one object of type ElementType.
  //!
  //! If out of memory, returns 0. 
  //!
  //! Amortized O(1).
  return static_cast<element_type *>(store().ordered_malloc());
}
void free BOOST_PREVENT_MACRO_SUBSTITUTION(element_type * const chunk)
{ //! De-Allocates memory that holds a chunk of type ElementType.
  //!
  //!  Note that p may not be 0.\n
  //!
  //! Note that the destructor for p is not called. O(N).
  store().ordered_free(chunk);
}

所以对于问题2:你不需要使用boost::pool<>::ordered_malloc在大多数情况下。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boost::pool<>::malloc 和 boost::pool<>::ordered_malloc 之间有什么区别,什么时候应该使用 boost::pool<>::ordered_malloc? 的相关文章

随机推荐