- PR -

CLRでSmallObjectAllocator

1
投稿者投稿内容
やっぷ
会議室デビュー日: 2007/05/07
投稿数: 17
投稿日時: 2008-11-26 20:00
お世話になります。やっぷと申します。

C++/CLI、C#のどちらでもよいのですが、CLR環境で、ネイティブのCやC++で使われるところのSmallObjectAllocator を実現する方法はあるのでしょうか。
勿論、CLR環境のガベージコレクションの話を知れば、SmallObjectAllocatorの実装については、
即座に絶望的であると思ったのですが、もしかして方法があるのであれば、知りたいと思い、
質問させて頂きます。
もちろん知りたいのは、C++/CLIで、native混在コードを書いて実現っていうことではなくて、
CLRpureなコードで実現する方法です。

(補足ながら、SmallObjectAllocator としてここで意味するのは、無数の小サイズのオブジェクトの
メモリアロケーションを効率的に行うための、例のテクニックです。
…こんな補足は必要なかったでしたらすみません。。)

もし、CLR環境でも、SmallObjectAllocatorのようなものが独自実装は無理としても
設定できるのであれば、今後CLRでの開発もバリバリやっていけると思っているのですが。。

ひとまず思いつくのは、でっかいメモリプールをgcnew でガッ!っと取っておいて、
その中を切り分けて使うという方法ですが、、もしそんなこと実現されている方がいましたらば、
先人がいるという事実だけでも知っておきたいのですが。。
ならばまずは自分でやってみればいいという説もありますがひとまず書き込ませて頂きました。
ではよろしくお願いします。

囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2008-11-27 00:06
SmallObjectAllocator って Modern C++ Design に記述されてるアレでしょうか。今、読み返して見ましたがイマイチよく分かりませんな・・・。というのは置いといて。
CLR はどっちにしろ最初に「ガッ」と最初にメモリを確保しておいて、C# の new や C++/CLI の gcnew は最初に「ガッ」と確保しておいたメモリ(マネージヒープ)から取得するので、C++ の new と比べると、格段にコストが低いと思います。
なんで、あまり意味ないかもしれないし、new 演算子がオーバーロードできるわけでもないので構文上の素敵さもない・・・。
ya
大ベテラン
会議室デビュー日: 2002/05/03
投稿数: 212
投稿日時: 2008-11-27 00:12
出来る出来ない以前におそらくあまり意味がない、有効な場面が非常に限られているかと。
CLR のようなフルセットな GC を持っている環境だとアローケーションのコストがもともと小さいし一定です(new は基本的にヒープのポインタをサイズ分進めるだけ、フラグメンテーションもコンパクションで解消)。まぁ参照の移動ができるのが大きいですな。さらにいえば、自己管理した分、システムから見れば余計な参照が残るわけですから世代への悪影響が発生してスループットが低下しかねません。

もちろん、GC はあくまで大域的で汎用的なシステムですから特定領域に限って意味がある可能性もあります(特に Compact Framework とかでは)。ただその視点で考えてもむしろそういう意味ではなくて値型配列にして「参照を減らそう」とかそういう視点になるかと(その場合でもLarge Object Heap に格納されるマイナス面を打ち消すだけの意味がなくてはなりません)。C/C++ の視点からの必要性とは異なります。

とりあえず基本知識としてガベージ コレクタの基本とパフォーマンスのヒントあたりが参考になるかと。この内容的には「割り当てるサイズの問題」あたりでしょうか。
古いドキュメントですから新しい話ないですし突っ込んでいる文章でもありませんので参考程度に。
やっぷ
会議室デビュー日: 2007/05/07
投稿数: 17
投稿日時: 2008-11-27 00:36
早速のレス、感謝です。
まずは実験してみることにします。

囚人さん:
それですModernC++に出てるやつです。あえてModernC++のというよりかは、むしろ一般的な概念と思ってますが。私の仕事してる業界なんかだと100万個単位のオブジェクトをがっとAllocateしたりすることはざらなので、必須の技術なのです、まぁある意味使い古されてるといえばそうなのですが。。gcnewってコスト低いのですね、知らなかった。

yaさん:
非常に興味深いリンクありがとうございます。
これはつまり何も考えずにgcnewでアロケートしてみろってことですね!いやはやまさか効率がnewよりも良いものだとは思いもつきませんでした。やりもせずに諦めて立ち止まっていた自分に反省です。

Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2008-11-27 00:53
こんばんは。

たしかマネージヒープへの領域の割り当ては、
スタック領域内でスタックポインタを加算するだけで領域確保するようなもので
かなり軽量な処理だと、どこかに書いてあったと思います。

ただ、100万個単位の小さなオブジェクトというのが、どんなものか気になりますけど。

デザインパターンのFlyweightパターンなんかは使えないものなんでしょうかね?
やっぷ
会議室デビュー日: 2007/05/07
投稿数: 17
投稿日時: 2008-11-28 20:52
dnr_symさん、
メモリを確保したい100万個のオブジェクトというのはその個々がちゃんと情報を持ってないといけないので、(個々のオブジェクトの大きさは大きいものでも100バイト未満です)FlyWeightパターンとかで回避できる話でもないです。ありがとうございます!

ところで実験してみました。以下が全実験コードです。実験では個々のオブジェクトのサイズを7200バイトまで膨らませて、超過酷なメモリ確保タスクとしてみました。(空のCLRプロジェクトで、.NETコンポーネントSystem, System::Windows::Formsを追加してビルドします。(あと、x64環境でないと通りません。Win32だと64Kの壁を越えるのでコンパイルエラーとなります。)
結果ですが、、私の驚きを越えていました。Nativeヒープのメモリ確保はそれなりに時間がかかっているにもかかわらず、マネージドヒープでのメモリ確保にかかった時間は、少なくとも本プログラムの測定精度では0ミリ秒でした。つまり、本当に本当に時間がかかってないのですね。っていうか、皆さんおっしゃってたようにCLRってあらかじめメモリをガッ!と取っていて、それを切り売りしてるだけの仕組みなんだってことが確認できました。
みなさん有難うございました。
コード:
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;
}

風になる
ベテラン
会議室デビュー日: 2008/07/28
投稿数: 85
投稿日時: 2008-12-06 11:45
(利用規約違反のため削除いたしました。@ITクラブメンバーシップセンター)
1

スキルアップ/キャリアアップ(JOB@IT)