Is there some way to use boost::obect_pool with faster free operations

前端 未结 1 1190
我在风中等你
我在风中等你 2021-01-17 06:48

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 indiv

1条回答
  •  滥情空心
    2021-01-17 07:34

    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 
    #include 
    #include 
    #include "time.h"
    #include 
    #include 
    
    struct foo {
        int data[10];
    };
    
    namespace non_boost {
        template 
        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 pool_type;
            typedef typename pool_type::size_type       size_type;
            typedef typename pool_type::difference_type difference_type;
    
            template 
            struct rebind {
                typedef fast_pool_allocator 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 
            fast_pool_allocator(const fast_pool_allocator & 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::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( (_ref->malloc)() ) 
                    : static_cast( _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( (_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 required to make the allocator standard-conforming.
        template
        class fast_pool_allocator {
        public:
            typedef void*       pointer;
            typedef const void* const_pointer;
            typedef void        value_type;
    
            template  struct rebind {
                typedef fast_pool_allocator other;
            };
        };
    
    }
    
    struct test {
        test(unsigned n) : size{ n } {}
        void run();
        float elapsedSec(clock_t& watch);
        unsigned size;
    
        boost::pool _pool { sizeof(foo) };
        non_boost::fast_pool_allocator _alloc { _pool };
    
        float mallocSec;
        float freeSec;
    };
    
    void test::run() {
        std::vector foos(size, nullptr);
        std::default_random_engine generator;
        std::uniform_int_distribution 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(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
    

    0 讨论(0)
提交回复
热议问题