免费调用在底层分配器上命中ordered_free。确实,ordered_malloc_need_resize()takes basically all the execution time。
我是否理解正确,我可以在 pool_allocator 和 fast_pool_allocator 之间进行选择,仅与 boost::singleton_pool 结合使用,而不与 boost::object_pool 结合使用?
是的。 object_pool 显然在底层simple_segregated_storage 上使用了ordered_free 函数。这显然是设计使然(尽管我暂时无法理解其基本原理。显然,在预期用途中,object_pool 始终针对数组分配/取消分配进行优化是有意义的)。
我现在理解的方式 pool_allocator 和 fast_pool_allocations 是独立的类,与 singleton_pool 的参数/选项无关。
是的。它们被硬连线以使用单例池实例。 Boost Pool 显然早于标准库对有状态分配器的支持。您可以将fast_pool_allocator 的实现复制出来以使用池的运行时实例而不是单例池。
以下示例使non_boost::fast_pool_allocator 成为特定“对象使用”池实例之上的有状态分配器。这使得分配器是有状态的。状态主要是指向池的指针。
_alloc.destroy 用于破坏foo 实例并释放内存。销毁_pool 后,任何未释放的元素仍将被释放(注意,因为我们不使用object_pool,在这种情况下不会运行foo 的析构函数。在您的示例中, foo 是 POS,因此很容易被破坏。如果不是,你当然可以使用 std::unique_ptr 或类似的,或者确实编写不坚持有序分配的 object_pool 版本。
演示
Live On Coliru
#include <boost/pool/pool.hpp>
#include <boost/pool/object_pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include "time.h"
#include <vector>
#include <random>
struct foo {
int data[10];
};
namespace non_boost {
template <typename T, typename UserAllocator = boost::default_user_allocator_new_delete>
class fast_pool_allocator
{
public:
typedef T value_type;
typedef UserAllocator user_allocator;
typedef value_type * pointer;
typedef const value_type * const_pointer;
typedef value_type & reference;
typedef const value_type & const_reference;
typedef boost::pool<UserAllocator> pool_type;
typedef typename pool_type::size_type size_type;
typedef typename pool_type::difference_type difference_type;
template <typename U>
struct rebind {
typedef fast_pool_allocator<U, UserAllocator> other;
};
pool_type* _ref;
public:
fast_pool_allocator(pool_type& ref) : _ref(&ref) { }
fast_pool_allocator(fast_pool_allocator const&) = default;
fast_pool_allocator& operator=(fast_pool_allocator const&) = default;
// Not explicit, mimicking std::allocator [20.4.1]
template <typename U>
fast_pool_allocator(const fast_pool_allocator<U, UserAllocator> & other) : _ref(other._ref)
{ }
// Default destructor used.
static pointer address(reference r) { return &r; }
static const_pointer address(const_reference s) { return &s; }
static size_type max_size() { return (std::numeric_limits<size_type>::max)(); }
void construct(const pointer ptr, const value_type & t) { new (ptr) T(t); }
void destroy(const pointer ptr) { ptr->~T(); }
bool operator==(fast_pool_allocator const& rhs) const { return _ref == rhs._ref; }
bool operator!=(fast_pool_allocator const& rhs) const { return _ref != rhs._ref; }
pointer allocate(const size_type n)
{
const pointer ret = (n == 1)
? static_cast<pointer>( (_ref->malloc)() )
: static_cast<pointer>( _ref->ordered_malloc(n) );
if (ret == 0)
boost::throw_exception(std::bad_alloc());
return ret;
}
pointer allocate(const size_type n, const void * const) { return allocate(n); }
pointer allocate()
{
const pointer ret = static_cast<pointer>( (_ref->malloc)() );
if (ret == 0)
boost::throw_exception(std::bad_alloc());
return ret;
}
void deallocate(const pointer ptr, const size_type n)
{
#ifdef BOOST_NO_PROPER_STL_DEALLOCATE
if (ptr == 0 || n == 0)
return;
#endif
if (n == 1)
(_ref->free)(ptr);
else
(_ref->free)(ptr, n);
}
void deallocate(const pointer ptr) { (_ref->free)(ptr); }
};
//Specialization of fast_pool_allocator<void> required to make the allocator standard-conforming.
template<typename UserAllocator>
class fast_pool_allocator<void, UserAllocator> {
public:
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template <class U> struct rebind {
typedef fast_pool_allocator<U, UserAllocator> other;
};
};
}
struct test {
test(unsigned n) : size{ n } {}
void run();
float elapsedSec(clock_t& watch);
unsigned size;
boost::pool<boost::default_user_allocator_malloc_free> _pool { sizeof(foo) };
non_boost::fast_pool_allocator<foo, boost::default_user_allocator_malloc_free> _alloc { _pool };
float mallocSec;
float freeSec;
};
void test::run() {
std::vector<foo *> foos(size, nullptr);
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(0, size - 1);
auto dice = std::bind(distribution, generator);
clock_t watch = clock();
for (unsigned i = 0; i < size; ++i)
foos[i] = _alloc.allocate();
mallocSec = elapsedSec(watch);
for (unsigned i = 0; i < size / 10;) {
auto idx = dice();
if (foos[idx] != nullptr)
{
_alloc.destroy(foos[idx]);
foos[idx] = nullptr;
}
i += 1;
}
freeSec = elapsedSec(watch);
}
float test::elapsedSec(clock_t& watch) {
clock_t start = watch;
watch = clock();
return (watch - start) / static_cast<float>(CLOCKS_PER_SEC);
}
int main() {
test t(10u << 20);
t.run();
std::cout << t.mallocSec << "\n";
std::cout << t.freeSec << "\n";
}
打印(在我的系统上):
0.135127
0.050991