2011年2月28日月曜日

object_poolのdestroy()がなんだか遅い

object_poolでメモリを確保したshared_ptrをvectorに大量に追加し、clear()で解放しようとした際、フリーズしたかと思うほど時間がかかったので検証してみた。

どうやら、destroy()の中で使われているordered_free()がなんかのタイミングでオーバーヘッドになってるっぽい。

以下、無駄に長い検証用コード。




#include <iostream>

#include <boost/shared_ptr.hpp>

#include <boost/pool/pool.hpp>
#include <boost/pool/object_pool.hpp>

#include <new>
#include <vector>

#include <time.h>
#include <sys/time.h>

using namespace std;
using namespace boost;

class testClass {
public:
 testClass() {}//cout<<"constructer"<<endl;}
 ~testClass(){}//cout<<"destructer"<<endl;}
};

//使用するpool,object_pool
pool<> p(sizeof(testClass));
object_pool<testClass> op;

// 時間計測用
double gettimeofday_sec()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + (double)tv.tv_usec*1e-6;
}

// pool malloc ////////////////////////////////////////
inline testClass* malloc_pool()
{
 testClass* temp = (testClass*)p.malloc();
 if (!temp) throw std::bad_alloc(); 
 return new(temp)testClass();     
}

// pool free //////////////////////////////////////////
inline void free_pool(testClass *ptr)
{
 ptr->~testClass();
 p.free(ptr);
}

// pool ordered_malloc ////////////////////////////////
inline testClass* malloc_pool_ordered()
{
 testClass* temp = (testClass*)p.ordered_malloc();
 if (!temp) throw std::bad_alloc(); 
 return new(temp)testClass();     
}

// pool ordered_free //////////////////////////////////
inline void free_pool_ordered(testClass *ptr)
{
 ptr->~testClass();
 p.ordered_free(ptr);
}

// object_pool construct //////////////////////////////
inline testClass* construct_object_pool()
{
 return op.construct();
}

// object_pool construct //////////////////////////////
inline void destroy_object_pool(testClass *ptr)
{
 op.destroy(ptr);
}

typedef boost::shared_ptr<testClass> shPtr;

// テスト用 object_poolでメモリ確保、解放を行うshared_ptrを生成
inline shPtr createShPtr()
{
 return shPtr(construct_object_pool(),destroy_object_pool);
}

// 後ろの要素から解放するclear
inline void clear_reverse(vector<shPtr> &v)
{
 vector<shPtr>::reverse_iterator rItr   = v.rbegin();
    vector<shPtr>::reverse_iterator rSentinel = v.rend();
 //ややこしい!
 while(rItr != rSentinel){
  rItr = vector<shPtr>::reverse_iterator(v.erase((++rItr).base()));
 }
}

/////////////////////////////////////////////////////////////////////////////
int main () {
    int max_count = 100000;
 double start, end;
 
 // 比較用 ふつーに new delete ////////////// 
 start = gettimeofday_sec();
 {
  testClass* tps[max_count];
  for (int i = 0; i < max_count; i++){
   tps[i] = new testClass();
  }
  for (int i = 0; i < max_count; i++){
   delete tps[i];
  }
 }
 end   = gettimeofday_sec();
 printf("%f new delete\n", end - start);
 
 // pool malloc free /////////////////////
 start = gettimeofday_sec();
 {
  testClass* tps[max_count];
  for (int i = 0; i < max_count; i++){
   tps[i] = malloc_pool();
  }
  for (int i = 0; i < max_count; i++){
   free_pool(tps[i]);
  }
 }
 end   = gettimeofday_sec();
 printf("%f pool malloc() free()\n", end - start);
 
 // pool ordered_malloc ordered_free /////
 start = gettimeofday_sec();
 {
  testClass* tps[max_count];
  for (int i = 0; i < max_count; i++){
   tps[i] = malloc_pool_ordered();
  }
  for (int i = 0; i < max_count; i++){
   free_pool_ordered(tps[i]);
  }
 }
 end   = gettimeofday_sec();
 printf("%f pool ordered_malloc() ordered_free()\n", end - start);
 
 // pool ordered_malloc ordered_free 逆順に解放
 start = gettimeofday_sec();
 {
  testClass* tps[max_count];
  for (int i = 0; i < max_count; i++){
   tps[i] = malloc_pool_ordered();
  }
  for (int i = max_count - 1; i >= 0; i--){
   free_pool_ordered(tps[i]);
  }
 }
 end   = gettimeofday_sec();
 printf("%f pool ordered_malloc() ordered_free() reverse\n", end - start);
 
 // object_pool construct destroy ///////
 start = gettimeofday_sec();
 {
  testClass* tps[max_count];
  for (int i = 0; i < max_count; i++){
   tps[i] = malloc_pool_ordered();
  }
  for (int i = 0; i < max_count; i++){
   free_pool_ordered(tps[i]);
  }
 }
 end   = gettimeofday_sec();
 printf("%f object_pool construct() destroy()\n", end - start);
 
 // object_pool construct destroy 逆順に解放///////
 start = gettimeofday_sec();
 {
  testClass* tps[max_count];
  for (int i = 0; i < max_count; i++){
   tps[i] = malloc_pool_ordered();
  }
  for (int i = max_count - 1; i >= 0; i--){
   free_pool_ordered(tps[i]);
  }
 }
 end   = gettimeofday_sec();
 printf("%f object_pool construct() destroy() reverse\n\n", end - start);
 
 // object_pool vector<shared_ptr> clear() ////////////////
 start = gettimeofday_sec();
 {
  vector< shPtr > vec;
  vec.reserve(max_count);
  for (int i = 0; i < max_count; i++){
   vec.push_back(createShPtr());
  }
  vec.clear();
 }
 end   = gettimeofday_sec();
 printf("%f object_pool vector<shared_ptr> clear()\n", end - start);
 
 // object_pool vector<shared_ptr> clear_reverse() ////////////////
 start = gettimeofday_sec();
 {
  vector< shPtr > vec;
  vec.reserve(max_count);
  for (int i = 0; i < max_count; i++){
   vec.push_back(createShPtr());
  }
  clear_reverse(vec);
 }
 end   = gettimeofday_sec();
 printf("%f object_pool vector<shared_ptr> clear_reverse()\n", end - start);
    return 0;
}

実行結果
0.019162 new delete
0.001466 pool malloc() free()
3.160949 pool ordered_malloc() ordered_free()
0.000657 pool ordered_malloc() ordered_free() reverse
8.477591 object_pool construct() destroy()
0.000578 object_pool construct() destroy() reverse

7.018725 object_pool vector<shared_ptr> clear()
0.018434 object_pool vector<shared_ptr> clear_reverse()

ordered_freeのコードを見てないのでなんともだけど、メモリを確保した順に解放しようとするとものすごく時間がかかる模様。名前から察するに後ろから解放していけばいいんじゃね?的な勘でやってみたら上手くいった。

暇なときに、poolのアルゴリズムに目を通しておきたいですね。

0 件のコメント:

コメントを投稿