2016-12-12 1 views
-2

私はフリーメモリチャンクのリストとして表現するメモリプールを持っており、事前割り当てメモリにオブジェクトを格納するために、このオブジェクトを個別に割り当てるよりも高速かもしれないので、このチャンクを "placement new"事前に割り当てられたメモリにオブジェクトを作成するのはなぜですか?オブジェクトを個別に割り当てることでオブジェクトを作成するのはなぜですか?

「新しい」キーワードを使用してオブジェクトを作成するコードスニペットが2つあります。

最初にコードがスローされました:メモリプールによって提供されたメモリを使用して、 "配置new"で事前割当メモリで作成されたオブジェクト。 2番目のコードがスローされました。事前割り当てメモリを使用せずに作成したオブジェクト。「新規」のみです。

プレースメントに基づいて切り取られた理由が、プレースメントなしで切り取られたものより新しいのはなぜですか?

まず切り取ら:

#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

正確なコンパイラのバージョンとコンパイルコマンドを教えてください。最適化されていない/デバッグビルドのパフォーマンスはまったく意味がないことに注意してください。 clangは2番目の例のループ全体を最適化することにも注意してください。 –

+1

C++のマイクロベンチマークでは、[this talk](https://youtu.be/nXaxk27zwlk)などの学習資料を見たいと思っています。考えているよりも難しいです。 –

+0

私はgccコンパイラを使用しています。http://rextester.com/DOS20329 –

答えて

1

を切り取ら)3つのもの:

  1. メモリアライメント

  2. プールがスコープ外になったときに割り当てられたオブジェクトを破棄します。

  3. インプレースnew

私はここにこれを試みたと同等の試験

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; 
    } 
} 
リンゴ打ち鳴らす8でコンパイル3yr歳のimacに

結果を書いたの戻り値 - O2:

with memory pool: 59 ms 
with new  : 842 ms 
+0

Upvoted、しかしちょうどOPの質問に答える。 OPの実装は、バッファ全体を事前に割り当てなかったため遅くなりました。一度に1つのオブジェクトにメモリを割り当ててから、新しい配置を呼び出します。したがって、このソリューションは、事前割り当てを高速化します。 –

+0

@TonyJ OPのプールインプリメンテーションは遅いですが、彼の言うことにもかかわらず、より速いです。 –

+0

@lideeveloper OPの実装も間違っているので、彼の測定値は無関係です。正しい実装を提供し、なぜそれがより速いのかを説明することははるかに役立ちます。 –

関連する問題