是否有某种方式使用boost :: obect_pool更快免费运营 [英] Is there some way to use boost::obect_pool with faster free operations

查看:153
本文介绍了是否有某种方式使用boost :: obect_pool更快免费运营的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用升压object_pool一段时间,普遍满意的结果。 previously我主要是分配各个对象,但很少单独释放他们,刚刚释放整个池一次。最近,当我遇到了需要释放从池中的许多对象来我发现,这是很缓慢的。

显然池是通过已经发布块列表中搜索到新发布的对象链接。关于有序和无序池的文档会谈,并提到pool_allocator以及fast_pool_allocator。无序池(使用fast_memory_allocator)presumably将是释放内存块要快得多。不过,我看不出我怎么才能利用它。

我是否理解正确的话,我只用升压:: singleton_pool结合,但不与升压:: object_pool有pool_allocator和fast_pool_allocator之间进行选择?

下面是说明问题的一个小的测试程序。它与VS2013建成并提振1_57_0。测试对象池分配n个对象,然后随机释放10%。它有一些原油时机仪表这表明对于n == 100000分配需要0.004秒,而释放需要0.4秒。同时对于n ==百万需要0.022秒来分配和42秒释放我的机器上。

 的#include<升压/游泳池/ object_pool.hpp>
的#includetime.h中
#包括LT&;矢量>
#包括LT&;随机的GT;结构foo的{
    int数据[10];
};结构试验{
    测试(无符号N):大小{N} {}
    无效的run();
    浮elapsedSec(clock_t表示&安培;观看);
    无符号的大小;
    提高:: object_pool<富> _pool;
    浮mallocSec;
    浮freeSec;
};无效测试::运行(){
    的std ::矢量<富* GT; FOOS(大小,nullptr);
    的std :: default_random_engine发生器;
    的std :: uniform_int_distribution< INT>分布(0,大小 - 1);
    汽车骰子=的std ::绑定(分销,发电机);
    clock_t表示手表=时钟();
    的for(int i = 0; I<大小; ++ I)
        FOOS [I] = _pool.malloc();
    mallocSec = elapsedSec(手表);
    的for(int i = 0; I<尺寸/ 10;){
        汽车IDX =骰子();
        如果(FOOS [IDX] == nullptr)
            继续;
        _pool.free(FOOS [IDX]);
        FOOS [IDX] = nullptr;
        我+ = 1;
    }
    freeSec = elapsedSec(手表);
}漂浮试验:: elapsedSec(clock_t表示&安培;观看){
    clock_t表示开始=手表;
    观看=时钟();
    回报(手表 - 开始)/ - 的static_cast LT;浮动>(CLOCKS_PER_SEC);
}


解决方案

该免费电话点击 ordered_free 底层分配器。事实上, ordered_malloc_need_resize() 基本上通吃的执行时间


  

我是否理解正确的话,我只用升压:: singleton_pool结合,但不与升压:: object_pool?

有pool_allocator和fast_pool_allocator之间的选择

是的。在 object_pool 显然使用 ordered_free 功能于底层 simple_segregated_storage 。这显然​​是由设计(尽管理由逃脱我的时刻。显然,在预期的使用情况是有意义的 object_pool 总是优化阵列分配/去分配)。


  

路上,我明白的事情现在pool_allocator和fast_pool_allocations是站在自己的,并没有涉及到singleton_pool的参数/选项类。


是的。他们是硬连接使用单池实例。升压游泳池清楚predates有状态的分配器标准库的支持。你可以复制 fast_pool_allocator 实施了用池而不是单池的运行实例。

下面的示例品牌 non_boost :: fast_pool_allocator A的状态的一个特定的对象使用池实例之上分配器。这使得THA分配状态。状态,主要是一个指针到池中。

_alloc.destroy 用于销毁的实例,并释放内存。任何unfreed元素仍将于 _pool 注意,因为我们不使用 object_pool 富将在这样的情况下运行。在你的样品,是POS因此平凡破坏。如果没有,你当然可以使用的std ::的unique_ptr 或类似的,或确实写一个版本 object_pool 不订购的分配坚持)。

DEMO

<大骨节病> 住在Coliru

 的#include&LT;升压/游泳池/ pool.hpp&GT;
#包括LT&;升压/游泳池/ object_pool.hpp&GT;
#包括LT&;升压/游泳池/ pool_alloc.hpp&GT;
的#includetime.h中
#包括LT&;矢量&GT;
#包括LT&;随机的GT;结构foo的{
    int数据[10];
};命名空间non_boost {
    模板&LT; typename的T,typename的UserAllocator =提振:: default_user_allocator_new_delete&GT;
    类fast_pool_allocator
    {
      上市:
        的typedefŧVALUE_TYPE;
        的typedef UserAllocator user_allocator;        的typedef VALUE_TYPE *指针;
        typedef的常量VALUE_TYPE * const_pointer;
        typedef的VALUE_TYPE&安培;参考;
        typedef的常量VALUE_TYPE&安培;为const_reference;
        TYPEDEF提振::池&LT; UserAllocator&GT; pool_type;
        的typedef typename的pool_type :: size_type的SIZE_TYPE;
        的typedef typename的pool_type :: difference_type difference_type;        模板&LT; typename的U&GT;
        结构重新绑定{
            typedef的fast_pool_allocator&LT; U,UserAllocator&GT;其他;
        };        pool_type * _ref;
      上市:
        fast_pool_allocator(pool_type&安培; REF):_ref(安培; REF){}        fast_pool_allocator(fast_pool_allocator常量和放大器;)=默认值;
        fast_pool_allocator&安培;运算符=(const的fast_pool_allocator&安培)=默认值;        //不明确的,模仿的std ::分配器[20.4.1]
        模板&LT; typename的U&GT;
        fast_pool_allocator(常量fast_pool_allocator&LT; U,UserAllocator&GT;&安培;其他):_ref(other._ref)
        {}        //使用的默认析构函数。
        静态指针地址(参考R){返回和再现性; }
        静态const_pointer地址(为const_reference S){返回和放大器; S; }
        静态SIZE_TYPE MAX_SIZE(){返回(的std :: numeric_limits&LT;&size_type的GT; ::最大)(); }
        无效结构(常量指针PTR,常量VALUE_TYPE&amp; T公司){新(PTR)T(T); }
        无效销毁(常量指针PTR){ptr-&GT;〜T(); }        布尔运算符==(const的fast_pool_allocator&放大器; RHS)const的{返回_ref == rhs._ref; }
        布尔运算符=(const的fast_pool_allocator&放大器; RHS)!常量{返回_ref = rhs._ref!; }        指针分配(常量SIZE_TYPE N)
        {
            常量指针RET =(N == 1)
                ?的static_cast&所述;指针&GT((_ref-&GT;的malloc)())
                :的static_cast&所述;指针&GT;(_ref-&GT; ordered_malloc(正));
            如果(RET == 0)
                提高:: throw_exception(则为std :: bad_alloc());
            返回RET;
        }        指针分配(常量SIZE_TYPE N,常量无效* const的){返回分配(N); }
        指针分配()
        {
            常量指针RET =的static_cast&所述;指针&GT((_ref-&GT;的malloc)());
            如果(RET == 0)
                提高:: throw_exception(则为std :: bad_alloc());
            返回RET;
        }
        无效DEALLOCATE(常量指针PTR,常量SIZE_TYPE N)
        {#IFDEF BOOST_NO_PROPER_STL_DEALLOCATE
            如果(PTR == 0 ||ñ== 0)
                返回;
#万一
            如果(N == 1)
                (_ref-&GT;免费)(PTR);
            其他
                (_ref-&GT;免费)(PTR,N);
        }
        无效DEALLOCATE(常量指针PTR){(_ref-&GT;免费)(PTR); }
    };    fast_pool_allocator&LT的专业化//;无效&GT;使分配器符合标准的要求。
    模板&LT; typename的UserAllocator&GT;
    类fast_pool_allocator&LT;无效,UserAllocator&GT; {
    上市:
        无效的typedef *指针;
        的typedef常量无效* const_pointer;
        无效的typedef VALUE_TYPE;        模板&LT;类U&GT;结构重新绑定{
            typedef的fast_pool_allocator&LT; U,UserAllocator&GT;其他;
        };
    };}结构试验{
    测试(无符号N):大小{N} {}
    无效的run();
    浮elapsedSec(clock_t表示&安培;观看);
    无符号的大小;    提高::池&LT;提高:: default_user_allocator_malloc_free&GT; _pool {的sizeof(富)};
    non_boost :: fast_pool_allocator&LT;富,提振:: default_user_allocator_malloc_free&GT; _alloc {_pool};    浮mallocSec;
    浮freeSec;
};无效测试::运行(){
    的std ::矢量&lt;富* GT; FOOS(大小,nullptr);
    的std :: default_random_engine发生器;
    的std :: uniform_int_distribution&LT; INT&GT;分布(0,大小 - 1);    汽车骰子=的std ::绑定(分销,发电机);
    clock_t表示手表=时钟();    对(无符号I = 0; I&LT;大小; ++ I)
         FOOS [I] = _alloc.allocate();    mallocSec = elapsedSec(手表);    对(无符号I = 0; I&LT;尺寸/ 10;){
        汽车IDX =骰子();
        如果(FOOS [IDX]!= nullptr)
        {
            _alloc.destroy(FOOS [IDX]);
            FOOS [IDX] = nullptr;
        }
        我+ = 1;
    }    freeSec = elapsedSec(手表);
}漂浮试验:: elapsedSec(clock_t表示&安培;观看){
    clock_t表示开始=手表;
    观看=时钟();
    回报(手表 - 开始)/ - 的static_cast LT;浮动&GT;(CLOCKS_PER_SEC);
}诠释主(){
    t检验(10U&LT;&LT; 20);
    t.run();    性病::法院LT&;&LT; t.mallocSec&LT;&LT; \\ n;
    性病::法院LT&;&LT; t.freeSec&LT;&LT; \\ n;
}

打印(我的系统上):

  0.135127
0.050991

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.

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.

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?

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

解决方案

The free call hits ordered_free on the underlying allocator. Indeed, ordered_malloc_need_resize() takes basically all the execution time.

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?

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).

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.

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.

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 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).

DEMO

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天全站免登陆