メモリプールのインターフェース
某所で書いたメモリプールに関しての実装をメモがてら。
これに関して色々考えてみて、C++についての知識が足りないなー、と実感したり。
お話としては、普通にnewしていくのは重いのでメモリプール使おうぜー、って話です。その実装方法とか。
とは言ってもメモリプール自体はboost::poolに任せて、そのインターフェースについて考えていました。
メモリプールというのは、以下のように先にメモリをどかっと確保しておいて必要に応じてその一部を使おうぜ、ということです。
struct Pool{ bool is_used; Hoge *hoge; }; Pool pool[128]; void init() { for(int i=0;i<128;i++) { pool[i].is_used = false; pool[i].hoge = new Hoge; } } Hoge *my_malloc() { for(int i=0;i<128;i++) { if(!pool[i].is_used) { pool[i].is_used = true; return pool[i].hoge; } } return NULL; } void my_free(Hoge *ptr) { for(int i=0;i<128;i++) { if(ptr == pool[i].hoge) { pool[i].is_used = false; return; } } }
くそコードなのでこれを真似たりしないでください。一応、Hogeクラスをプールしておくコードです。
boost::poolというのは、このメモリプールをいい感じにやってくれるboostライブラリ中のクラスです。
以下そのboost::poolを使ったメモリ管理のコード。
最初やってた方法
class Hoge { static boost::pool<> memoryPool; Hoge(args); public: static Hoge *create(args) { void *ptr = memoryPool.malloc(); return new(ptr) Hoge(args); } static void destroy(Hoge *ptr) { ptr->~Hoge(); memoryPool.free(ptr); } }; boost::pool<> Hoge::memoryPool(sizeof(Hoge));
クラスHoge内にboost::pool::mallocとplacement newの処理をラッピングするクラスメソッドを準備し、それらを介してインスタンスを生成できるようにしていました。
使い方は以下のような感じです。
Hoge *hoge = Hoge::create(args); hoge->fugafuga(); Hoge::destroy(hoge);
ですが、この方法だとメモリプールが必要なクラスごとにいちいちcreateやらdestroyやら定義しなきゃいけなくてめんどくさい!
でも引数付きのコンストラクタ使いたい!という事で、某所で聞いた意見を合わせて以下のようにしました。
今やってる方法
template <class TYPE> class ObjectPool { static boost::pool<> &getPool() { static boost::pool<> pool(sizeof(TYPE)); return pool; } public: static TYPE *create() { void *ptr = getPool().malloc(); return new(ptr) TYPE; } template <class ARG1_TYPE> static TYPE *create(ARG1_TYPE arg1) { void *ptr = getPool().malloc(); return new(ptr) TYPE(arg1); } template <class ARG1_TYPE, class ARG2_TYPE> static TYPE *create(ARG1_TYPE arg1, ARG2_TYPE arg2) { void *ptr = getPool().malloc(); return new(ptr) TYPE(arg1, arg2); } static void destroy(TYPE *ptr) { ptr->~TYPE(); getPool().free(ptr); } };
すごい泥臭いです。
使い方はこんな感じ。
Hoge *hoge = ObjectPool<Hoge>::create<Fuga &, int>(fuga, 10); hoge->fugafuga(); ObjectPool<Hoge>::destroy(hoge);
こうすると、引数付きコンストラクタを実行してメモリプールからメモリを引っ張ってこれます。
が、引数の数が増えるたびにメソッドを増やさないといけないのが非常にアレです。
あと、個人的にTYPEで指定したクラスはObjectPoolを通じてのみインスタンスを生成できるようにしたかったので、
class Hoge { friend template <class TYPE> class ObjectPool; Hoge(args); ~Hoge(); public: void func(); }; ------ Hoge *hoge = ObjectPool<Hoge>::create();
としたかったのですが、これじゃ「ObjectPoolはHoge::Hogeにアクセス権無いよ!」と怒られました。
createがクラスメソッドなので、ObjectPoolクラスだけをfriend指定するだけじゃダメ、ということでしょうか。
アクセス権限に関してあんまり知らないのでどうすればいいのかさっぱりです…。
それとも、ここらへんはどこかしら妥協するしか無いんものなんでしょうか。