是否有一些方法使用boost :: obect_pool更快的自由操作 [英] Is there some way to use boost::obect_pool with faster free operations

查看:151
本文介绍了是否有一些方法使用boost :: obect_pool更快的自由操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用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).

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";
}

打印(在我的系统上):

Prints (on my system):

0.135127
0.050991

这篇关于是否有一些方法使用boost :: obect_pool更快的自由操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆