.NETフレームワークのCLRな環境の提供する、マネージドヒープというやつにおけるメモリアロケーションは非常に高速なのであった。これまでは、「GCなんて機構があるのだから、Nativeヒープを使ってのメモリ確保には、速度面、効率面では到底及ぶまい」と決め付け、見向きもしてこなかったのだが、果たして結果は真逆であった。
非常に話を簡単にして言うと、マネージドヒープはいわゆる本来のNativeヒープにおける巨大なメモリをガッ!と確保しておいて、その中の区分けした領域を提供するだけ、みたいなしくみになっているようなので、非常に高速、というか限りなく0msに近いパフォーマンスを示すのである。いわゆる、定数時間というやつだ。
以下に実験コードを示そう。いつものようにVS2008で、「空のCLRプロジェクト」を新規作成し、プロジェクトの参照設定で、System、System::Windows::Forms を.NETコンポーネントとして追加し、x64環境で以下のコードをビルド、実行してみてくれ。(Win32でやるとc2148が出るがこれはNativeTestのやつがスタックオーバーフローを起こしているだけなのかも知れないね。Win32の場合は7200を72するといいかも知れないぞ)
using namespace ::System; using namespace ::System::Windows; using namespace ::System::Windows::Forms; // コールバックの定義 public ref class TimerTest { public: TimerTest( void ){ m_count=0; } void SayYeah( System::Object^ obj, System::Timers::ElapsedEventArgs^ args ){ ++m_count; return; } static int m_count; }; class NativeTest{ public: NativeTest( void ){} ~NativeTest( void ){} public: char a[7200]; }; public ref class ManTest{ public: ManTest( void ){ m_a = gcnew array<char>(7200); } ~ManTest( void ){} public: array<char>^ m_a; }; void main( void ) { TimerTest^ tt = gcnew TimerTest; { System::Timers::Timer^ timer = gcnew System::Timers::Timer(); { timer->Interval = 1; timer->AutoReset = !0; timer->Elapsed += gcnew System::Timers::ElapsedEventHandler( tt, &TimerTest::SayYeah ); timer->Start(); } } // 実験開始 const int n_obj = 1000000; { // native heap test Console::WriteLine( "count is " +(TimerTest::m_count) ); NativeTest* nv = new NativeTest[ n_obj ]; Console::WriteLine( "count is " +(TimerTest::m_count) ); delete [] nv; Console::WriteLine( "count is " +(TimerTest::m_count) ); } { // managed heap test array<ManTest^>^ mv = gcnew array<ManTest^>( n_obj ); Console::WriteLine( "count is " +(TimerTest::m_count) ); } delete tt; return; }
いかがだろうか。メモリ確保時間テストは、NativeHeapの結果がそれなりに時間かかるのに対して、ManagedHeapを使った場合は、殆ど全くといっていいほど、時間がかかっていないのであった。
これなら、やりたいことがCLR環境でもぜんぜん出来そうだ。C#やC++/CLIの研究を進めていく動機づけが出来た。C++/CLIでは、SmallObjectAllocatorなんて全く必要なかったのである。
しかし喜ぶのはまだ早いかも知れない。こうして確保して作ったオブジェクトたちは複雑な参照を互いにしあうことになるのだが、そのような複雑な参照の更新の処理に、GCの機構のパフォーマンスが落ちたりはしないだろうか?。。というわけで、答えは更なる実験の向こうにある。