为什么在预关注内存中创建对象慢,而不是单独为每个对象分配内存的对象

Why creating objects in preallocated memory slow than creating objects with allocating memory for each object individually?

本文关键字:对象 内存 单独 分配 为什么 创建对象      更新时间:2023-10-16

我有一个内存池,它表示为免费内存块列表,我需要在"放置新"中使用这些块,因为将对象存储在预关注的内存中可能比为每个分配内存的内存更快单独对象。

我有两个代码段,使用"新"关键字创建对象。

第一个代码剪切:使用内存池提供的内存中的"位置新"内存中创建的对象。第二个代码剪切:在不使用预关注内存的情况下创建对象,只需使用"新"。

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

首先剪辑:

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

我以前从未写过记忆池,但我想您想(至少)(至少)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;
    }
}

在3岁旧iMac上与苹果clang 8,-o2:

编译的结果
with memory pool: 59 ms
with new        : 842 ms