是否有一些方法使用boost :: obect_pool更快的自由操作 [英] Is there some way to use boost::obect_pool with faster free operations
问题描述
我已经使用boost object_pool一段时间,并且通常对结果感到满意。以前,我主要分配单个对象,但很少单独释放它们,只是释放整个池一次。最近,当我遇到需要从池中释放许多物体,我发现它是非常慢。
I have been using boost object_pool for some time and was generally happy with the results. Previously I was mostly allocating individual objects but rarely freed them individually, just freed entire pool at once. Recently when I came across the need to free many objects from the pool I discovered that it is VERY slow.
显然,池正在通过已发布的块的列表进行搜索以链接新发布的对象。文档讨论有序池和无序池,并提到pool_allocator以及fast_pool_allocator。无序池(使用fast_memory_allocator)可能会更快释放内存块。但是,我不能看到我如何使用它。
Apparently pool is searching through the list of already released chunks to link the newly released object. The documentation talks about ordered and unordered pools and mentions pool_allocator as well as fast_pool_allocator. Unordered pool (using fast_memory_allocator) presumably would be much faster in releasing memory chunks. However, I can't see how I can make use of it.
我明白我在pool_allocator和fast_pool_allocator之间有一个选择,只能与boost :: singleton_pool结合使用,但不能与boost :: object_pool结合使用?
下面是一个小测试程序说明了这个问题。它是用VS2013和boost 1_57_0。测试在对象池中分配n个对象,然后随机释放10%。它有一些粗略的计时仪表,显示对于n == 100,000分配需要0.004秒,而释放需要0.4秒。同时对于n == 1,000,000它需要0.022秒分配和42秒在我的机器上释放。
Below is a small test program illustrating the problem. It was built with VS2013 and boost 1_57_0. Test allocates n objects in object pool and then randomly releases 10%. It has some crude timing instrumentation which shows that for n == 100,000 allocation takes 0.004 sec while release takes 0.4 sec. Meanwhile for n == 1,000,000 it takes 0.022 sec to allocate and 42 sec to release on my machine.
#include <boost/pool/object_pool.hpp>
#include "time.h"
#include <vector>
#include <random>
struct foo {
int data[10];
};
struct test {
test(unsigned n) : size{ n } {}
void run();
float elapsedSec(clock_t& watch);
unsigned size;
boost::object_pool<foo> _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 (int i = 0; i < size; ++i)
foos[i] = _pool.malloc();
mallocSec = elapsedSec(watch);
for (int i = 0; i < size / 10;) {
auto idx = dice();
if (foos[idx] == nullptr)
continue;
_pool.free(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);
}
推荐答案
c $ c> ordered_free 。确实, ordered_malloc_need_resize()
基本上执行所有的执行时间。
The free call hits ordered_free
on the underlying allocator. Indeed, ordered_malloc_need_resize()
takes basically all the execution time.
我理解我可以选择pool_allocator和fast_pool_allocator只能与boost :: singleton_pool不是与boost :: object_pool?
Do I understand correctly that I have a choice between pool_allocator and fast_pool_allocator only in conjunction with boost::singleton_pool but not with boost::object_pool?
是的。 object_pool
明确使用底层 simple_segregated_storage
上的 ordered_free
。这显然是通过设计的(虽然基本原理逃避了我的一刻。显然在预期的用法是有意义的 object_pool
总是优化数组分配/取消分配)。
Yes. The object_pool
manifestly uses the ordered_free
function on the underlying simple_segregated_storage
. This is clearly by design (although the rationale escapes me for the moment. Apparently in the intended usage it makes sense for object_pool
to always optimize for array allocations/de-allocations).
我现在理解的方式pool_allocator和fast_pool_allocations是独立的类,并且与singleton_pool的参数/选项无关。 / p>
The way I understand things now pool_allocator and fast_pool_allocations are classes that stand on their own and are not related to arguments/options of singleton_pool.
是的。他们硬连线使用单例池实例。 Boost池显然早于标准库支持状态分配器。您可以复制 fast_pool_allocator
的实现以使用池的运行时实例,而不是单例池。
Yes. They are hardwired to use the singleton pools instances. Boost Pool clearly predates standard library support for stateful allocators. You could copy the implementation of fast_pool_allocator
out to use a runtime instance of pool instead of the singleton pool.
以下示例在特定对象使用池实例上方创建 non_boost :: fast_pool_allocator
a有状态分配器。这使得分配器有状态。状态主要是指向池的指针。
The following sample makes non_boost::fast_pool_allocator
a stateful allocator on top of a specific "Object Use" pool instance. That makes tha allocator stateful. The state is mainly a pointer to the pool.
_alloc.destroy
用于销毁 foo
实例并释放内存。由于我们不使用 object_pool $>,因此破坏
_pool
(注意在这个例子中, foo
是POS的一部分,而不是 foo
如果没有,你当然可以使用 std :: unique_ptr
或类似的,或者确实写一个版本 object_pool
不坚持有序分配)。
_alloc.destroy
is used to destruct the foo
instance and free the memory. Any unfreed elements will still be freed upon destruction of the _pool
(Note since we don't use object_pool
, no destructors for foo
will be run in such a case. In your sample, foo
is POS and therefore trivially destructible. If not, you can of course use a std::unique_ptr
or similar, or indeed write a version of object_pool
that doesn't insist on ordered allocation).
#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";
}
打印(在我的系统上):
Prints (on my system):
0.135127
0.050991
这篇关于是否有一些方法使用boost :: obect_pool更快的自由操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!