为什么在预分配内存中创建对象比创建为每个对象分别分配内存的对象慢?

时间:2016-12-12 17:19:17

标签: c++ memory memory-management

我有一个内存池,表示可用内存块的列表,我需要在“placement new”中使用这个块,因为在预分配的内存中存储对象可能比为每个对象单独分配内存要快。

我有两个代码段,使用“new”关键字创建对象。

第一个代码剪切:在预分配内存中创建的对象使用内存池提供的内存“placement new”。 第二个代码剪切:在不使用预分配内存的情况下创建对象,只使用“new”。

为什么基于新的缓慢放置剪切而不是不放置新的剪切?

首先剪断:

#include <chrono>
#include <iostream>

struct list
{
    list *next;
};

class Test
{
    public:
    int a;
    int b;
};

void* getPtr()
{
    static int init = 0;
    static list *head;
    static list *free;
    if (!init) {
        std::cout << "Initialized." << std::endl;
        init = 1;
        list *head = reinterpret_cast<list*>(new char(sizeof(Test)));
        free = head;
        for (int i = 0; i < 10000000; i++) {
            head->next = reinterpret_cast<list*>(new char(sizeof(Test)));
            head = head->next;
        }
    }

    list *ret = free;
    free = ret->next; 
    return ret;
}

int main()
{
    getPtr();
    auto begin = std::chrono::high_resolution_clock::now();
    for(int i = 0; i < 10000000; i++) {
         new(getPtr())Test();
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout<<std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count()<<" ns"<< std::endl;
}

第二次剪辑

#include <chrono>
#include <iostream>

class Test
{
    public:
    int a;
    int b;
};

int main()
{
    auto begin = std::chrono::high_resolution_clock::now();
    for(int i = 0; i < 10000000; i++) {
         new Test();
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout<<std::chrono::duration_cast<std::chrono::nanoseconds>(end-begin).count()<<" ns"<< std::endl;
}

1 个答案:

答案 0 :(得分:2)

我之前从未写过内存池,但我认为你会考虑(至少)3件事:

  1. 内存对齐

  2. 当游泳池超出范围时,销毁已分配的对象。

  3. 就地new

  4. 的返回值

    我在这里尝试了这个并写了一个等效的测试

    struct destroy_not_free
    {
        template<class T>
        void operator()(T* p) const { p->~T(); }
    };
    
    template<class Jobbie>
    struct memory_pool {
        using buffer_type = std::aligned_storage<sizeof(Jobbie), alignof(Jobbie)>;
    
        memory_pool(std::size_t limit)
                : memptr_(std::make_unique<buffer_type[]>(limit)), limit_(limit), used_(0)
        {
            allocated_.reserve(limit);
        }
    
        memory_pool(const memory_pool&) = delete;
        memory_pool(memory_pool&&) = default;
        memory_pool& operator=(const memory_pool&) = delete;
        memory_pool& operator=(memory_pool&&) = delete;
    
        template<class...Args>
        Jobbie *create(Args &&...args) {
            if (used_ < limit_) {
                auto candidate = new(std::addressof(memptr_[used_])) Jobbie(std::forward<Args>(args)...);
                allocated_.emplace_back(candidate);
                ++ used_;
                return candidate;
            }
            else {
                throw std::bad_alloc();
            }
        }
    
    
        // NOTE: order important. We want the vector of unique_ptr to 
        // be destroyed before the memory buffer otherwise calling the
        // destructors will result in software armageddon
    
        std::unique_ptr<buffer_type[]> memptr_;
        std::vector<std::unique_ptr<Jobbie, destroy_not_free>> allocated_;
        std::size_t limit_;
        std::size_t used_;
    };
    
    class Test {
    public:
        int a;
        int b;
    };
    
    int main() {
        {
            auto pool = memory_pool<Test>(10000000);
            auto begin = std::chrono::high_resolution_clock::now();
            for (int i = 0; i < 10000000; i++) {
                pool.create();
            }
            auto end = std::chrono::high_resolution_clock::now();
            std::cout << "with memory pool: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << " ms" << std::endl;
        }
        {
            std::vector<std::unique_ptr<Test>> v;
            v.reserve(10000000);
            auto begin = std::chrono::high_resolution_clock::now();
            for (int i = 0; i < 10000000; i++) {
                v.emplace_back(new Test());
            }
            auto end = std::chrono::high_resolution_clock::now();
            std::cout << "with new        : " << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << " ms" << std::endl;
        }
    }
    

    使用apple clang 8编译3yr old imac,-O2:

    with memory pool: 59 ms
    with new        : 842 ms