Boost.Poolを試してみた
ゲームで使いたいBoost.Pool…だけど聞こえてくる噂はどうにも微妙…?
全ゲ連の講演とかでも時折聞いたりするものの、イマイチ「いいよ!! マジおすすめ!」っていう程のトーンで話を聞かない。
そんな中、ゲームプログラミングにおけるC++の都市伝説がTwitterで流れてきたので読んでいたら、なんとBoost.Poolの話があった!
それで、いろいろ納得できたり、もうちょっと挙動知りたかったりだったので、自分で試してみた。
Boost.Poolは内部で確保したメモリ領域を小分けにして使うことでnewが早く………
とか、よく聞くのだけれど、つまりは
「事前に用意してあるメモリ領域に」
「変なコトにならないように自動で」
「placement newする」
みたいなものっぽい。
で、パフォーマンス的にどんなもんじゃろ? という事で実測してみた。
10万回newして、毎回どのくらい時間かかっているかを出力。
コードがアレだけど見逃して…
#include <iostream> #include <memory> #include <list> #include <boost/lexical_cast.hpp> #include <boost/pool/object_pool.hpp> #include <boost/chrono/system_clocks.hpp> typedef boost::chrono::high_resolution_clock clock_type; class Hoge{ public: Hoge(std::string name) { static int i=0; i++; name_ = name + boost::lexical_cast<std::string>(i); } void print() { std::cout << name_ << std::endl; } ~Hoge() { // std::cout << name_ << " deleted" << std::endl; } private: std::string name_; char tmp[1024]; }; #include <fstream> int main() { std::ofstream ofs("test.csv", std::ios::app | std::ios::ate); for(int s=0; s < 1; ++s){ boost::object_pool<Hoge> mem_pool(100000); { // Hogeを格納するlist std::list<std::shared_ptr<Hoge>> hoge_list; for(int i=0; i < 100000; ++i){ clock_type::time_point start = clock_type::now(); #ifndef _ // new (pool) Hoge* tmp = new Hoge("taro"); // shared_ptrにくるむ auto object = std::shared_ptr<Hoge>( tmp); #else // new (pool) Hoge* tmp = mem_pool.construct("taro"); // shared_ptrにくるむ auto object = std::shared_ptr<Hoge>( tmp, // 通常のdeleteはできないので、デリータで解放するようにしておく // ラムダ式にはmem_poolの参照をバインドしておく [&mem_pool](Hoge* hoge) { mem_pool.destroy(hoge); } ); #endif // push_back hoge_list.push_back( object ); boost::chrono::nanoseconds elapsed = clock_type::now() - start; ofs << i << "," << elapsed.count() << "," << std::endl; } }// hoge_list内のオブジェクト全てのデストラクタが走る } std::cout << "END" << std::endl; return 0; }
結果は一番下に載せておく。
結果から見ると、メモリ領域の確保はstd::vectorと同じく「足りなくなったら既存領域のn倍を再確保」しているっぽい。MSVCでやったので、nは1.5。gccなら2。
ナマでnewするのに比べて、newにかかる時間の振れ幅はかなり抑えられているようだ。
最初の確保数を「1」とかの小さな値にすると、再確保が起きる度にガクッと処理が遅くなるので、これが所謂「時折ガクッとなるからゲームには使えない」という噂のモトっぽい。
結論としては、
「newするたびにカクッとなられるのは困るんだけど、自分でアロケータ書くの嫌だああああ! という時には使えるんじゃないですかね?」
という感じでした。
平均して凄い時間かかってるように見えるが、横は10万要素あるので「n個に1個の特別時間かかったの」が見えているだけ。逆に言えば、見えてない下のは「n個に1個」の頻度が少ないことがわかる。
最初に全部領域を確保しているせいか、最初だけアホみたいに重い。
やってはいけない典型例。無駄な再確保が行われている。あと1個要素を追加すれば、再確保が起きて一個上の完全劣化。