Boost.Pool

#include <cstddef>
#include <list>
#include <iostream>

#include <boost/pool/pool_alloc.hpp>
#include <boost/timer.hpp>

int main()
{
  int const n = 10000000;

  boost::timer t;
  double d = 0.0;
  {
    std::list< int, ***** ここを色々取り替える ***** > l;
    for( int i = 0; i < n; ++i ){
      l.push_back( 42 );
      //l.pop_back(); // allocation/dealloation の繰り返しではここを comment in.
    }
    d = t.elapsed();
    std::cout << d << std::endl;
  }
  std::cout << "on destruction: " << t.elapsed() - d << std::endl;

  return EXIT_SUCCESS;
}

MSVC7.1 on Windows XP, Athlon 2500+

シングルスレッドビルド
  • allocation/deallocation の繰り返し
std::allocator 3.890s
boost::pool_allocator 3.156s
boost::fast_pool_allocator 0.468s
  • allocation を繰り返して最後に deallocation
std::allocator 3.046s + 3.204s
boost::pool_allocator N/A
boost::fast_pool_allocator 1.203s + 0.343s
マルチスレッドビルド
  • allocation/deallocation の繰り返し
std::allocator 3.937s
boost::pool_allocator 3.843s
boost::fast_pool_allocator 1.093s
  • allocation を繰り返して最後に deallocation
std::allocator 5.406s + 4.844s
boost::pool_allocator N/A
boost::fast_pool_allocator 1.578s + 0.765s

GCC4.1.1 on Linux, XEON 2.4GHz x2

シングルスレッドビルド
  • allocation/deallocation の繰り返し
std::allocator 1.08s
boost::pool_allocator 2.24s
boost::fast_pool_allocator 0.56s
  • allocation を繰り返して最後に deallocation
std::allocator 1.15s + 0.47s
boost::pool_allocator N/A
boost::fast_pool_allocator 0.64s + 0.22s
マルチスレッドビルド
  • allocation/deallocation の繰り返し
std::allocator 1.15s
boost::pool_allocator 4.78s
boost::fast_pool_allocator 3.20s
  • allocation を繰り返して最後に deallocation
std::allocator 1.14s + 0.46s
boost::pool_allocator N/A
boost::fast_pool_allocator 2.01s + 1.53s

結果

単純かつ理想的なコードなのであんまり意味が無いけれどキニシナイ!! とりあえず boost::pool_allocator はチャンク数が増えると急激にパフォーマンスが悪化することは分かった(deallocation が激しく遅くなる).
あくまで自分の環境での結果として,

  • Windows 環境では一定して Boost.Pool が outperform.特に, fast_pool_allocator による改善は著しい (小規模チャンクのみで評価しているから, fast_pool_allocator 有利なのは当たり前だけれど)
  • GCC 環境ではシングルスレッド環境で Boost.Pool が outperform する可能性はある.しかし,マルチスレッド環境ではデフォルトの allocation に勝てる見込みはなさそう

な感じのまとめか.上のコードは (典型的な 32bit 環境では) 12バイトのリストのノードの allocation/deallocation なので一定して pool_allocator より fast_pool_allocator 優位だけれど,連続した中規模・大規模のチャンクの allocation/deallocation には,ドキュメントのとおり pool_allocator 使ってね.
しかし, allocator は排他処理だから threading の設定で状況が変わりうるとはいえ, GCC マルチスレッドでの Boost.Pool の結果はへぼすぎ. Boost.Pool の排他処理がへぼいのか,はたまた libstdc++ のデフォルト allocator がカリカリモフモフにチューンされているのか.
いずれにせよ,結局のところは低レベルな部分で簡単に allocator を取り替えられるようにしておいて,ある程度の規模に組みあがってテストもきっちり走るようになってから allocator を取り替えてみて,パフォーマンスを比較して総合的に判断してね,としか書きようがない.

GCC (libstdc++) 付属の STL をマルチスレッド環境で使うにはどういう設定が要るんでしょうか?という基本的な疑問

あったあった.


When you link a multithreaded application, you will probably need to add a library or flag to g++. This is a very non-standardized area of GCC across ports. Some ports support a special flag (the spelling isn't even standardized yet) to add all required macros to a compilation (if any such flags are required then you must provide the flag for all compilations not just linking) and link-library additions and/or replacements at link time. The documentation is weak. Here is a quick summary to display how ad hoc this is: On Solaris, both -pthreads and -threads (with subtly different meanings) are honored. On OSF, -pthread and -threads (with subtly different meanings) are honored. On Linux/i386, -pthread is honored. On FreeBSD, -pthread is honored. Some other ports use other switches. AFAIK, none of this is properly documented anywhere other than in ``gcc -dumpspecs'' (look at lib and cpp entries).
上にプラスしてこれか.

However, please ignore all discussions about the user-level configuration of the lock implementation inside the STL container-memory allocator on those pages. For the sake of this discussion, libstdc++-v3 configures the SGI STL implementation, not you. This is quite different from how gcc pre-3.0 worked. In particular, past advice was for people using g++ to explicitly define _PTHREADS or other macros or port-specific compilation options on the command line to get a thread-safe STL. This is no longer required for any port and should no longer be done unless you really know what you are doing and assume all responsibility.
GCC 3.0 以降の場合「_PTHREADS define しろ」とかいうアドバイスは古い,と.
あと, SGI の実装なので当然これこれ (前者は以前,どなたかにコメントで教えていただいたことがあるはず).
やはりさすがに自分がお世話になっている環境のドキュメントぐらいは読み込んでおかないとダメカナ?