Farseer 2.xが一時ではなく、メンバーとしてスタックを格納するのはなぜですか?(。ネット)


10

更新:この質問は、Farseer 2.xに関するものです。新しい3.xではこれができないようです。

現在、Farseer Physics Engineを非常に広範囲に使用していますが、クラスのメンバーとして多くの一時的な値の型を格納しているように見え、期待どおりにスタックに格納されていないようです。

これはBodyクラスの例です:

private Vector2 _worldPositionTemp = Vector2.Zero;

private Matrix _bodyMatrixTemp = Matrix.Identity;
private Matrix _rotationMatrixTemp = Matrix.Identity;
private Matrix _translationMatrixTemp = Matrix.Identity;

public void GetBodyMatrix(out Matrix bodyMatrix)
{
    Matrix.CreateTranslation(position.X, position.Y, 0, out _translationMatrixTemp);
    Matrix.CreateRotationZ(rotation, out _rotationMatrixTemp);
    Matrix.Multiply(ref _rotationMatrixTemp, ref _translationMatrixTemp, out bodyMatrix);
}

public Vector2 GetWorldPosition(Vector2 localPosition)
{
    GetBodyMatrix(out _bodyMatrixTemp);
    Vector2.Transform(ref localPosition, ref _bodyMatrixTemp, out _worldPositionTemp);
    return _worldPositionTemp;
}

手作業によるパフォーマンス最適化のようです。しかし、これがパフォーマンスにどのように役立つかわかりませんか?(もし私がオブジェクトをはるかに大きくすることでそれが傷つくと思うなら)。

回答:


6

.NETでは値型はスタックに格納されるため、割り当てコストは最小限になりますが、初期化のコストがなくなるわけではありません。

この場合、1つまたは2つの一時行列を使用する一連の関数があるため、呼び出しごとに16〜32の浮動小数点数が初期化されます。これは重要ではないように見えるかもしれませんが、メソッドが十分に頻繁に使用される場合(たとえば、フレームあたり数千回、数千回)、合計オーバーヘッドは意味のある影響を与える可能性があります。そのような手法がすべてのそのような方法にわたって体系的に使用される場合、排除されるオーバーヘッドはかなりのものになる可能性があります。

このような手法を使用すると、オブジェクトごとのレベルでスレッドセーフを提供する機能がなくなりますが、そのような細かいレベルで保証を提供することは一般的に賢明ではありません。


よろしいですか?私が考えたのは、コンストラクターを呼び出さないようにすることかもしれないということでした。ただし、値型の場合、すべてのメンバーを設定する(またはoutパラメーターとして渡す)場合、コンストラクター(デフォルトのパラメーターなしのコンストラクターを含む)を呼び出す必要はありません。このルールの要点は、コンパイラがそのメモリのゼロ化をスキップできるようにするためだと私はかなり確信しています。(スタックポインターの移動が本当に遅いのですか?)
Andrew Russell、

意外ですね。残念ながら、生成されたILを検査すると、一時行列が初期化されます。いくつかの簡単なテストでは、メンバーの一時バージョンが10〜15%速いことが示されています。
Jason Kozak 2010

1
私は唖然。「Understanding XNA Framework Performance」(GDC2008)で、Shawn Hargreavesは構造体について次のように述べています。ゼロに初期化するために '"。これが私の情報の出所です。しかし、今再び聞くと、彼は「普通に」しか言いません。プレゼンテーションのすぐ次のポイントは、JITがデバッガーを接続した状態で異なる動作をし、パフォーマンスに影響を与えることです(どのようにテストしましたか?)。また、彼はここでJITについて話しているので、おそらくILは「ナイス」のままです(検証可能性?)。
Andrew Russell

ILはReflectorを介して検査され、テストはIDE組み込みリリースの外部で実行されました(Windowsでは、テスト対象のCCメンバーシップはもうありません)
Jason Kozak 2010

1
これに基づいて-それらのメンバーを一時的なものにするstatic(および/または積極的に再利用する)ほうが良い(そしてどれだけ優れている)のかと思います。たとえば、BodyFarseer のクラスには、73個のfloatに相当する「不要な」メンバーがあります。
Andrew Russell

-1

良い質問。私はかなり鋭いC#/。NETの人であり、少しパフォーマンスナットです、そしてこれは私にはかなり奇妙なデザイン決定のようです。私に飛びつく最初のことは、このコードは決してスレッドセーフではないということです。それが物理システムの問題かどうかはわかりませんが、メソッドの範囲外に一時データを保存することは、多くの場合、災害の原因になります。

正直なところ、サードパーティのフレームワークでこの種のコードに定期的に遭遇した場合、おそらく別のフレームワークを見つけようとするでしょう。


3
質問には本当に答えません。
Brian Ortiz、

ええ、私ができる最善のことは彼が狂っていないことを確認することです、そしてそれがそのようにコード化されていることには実際の利益はないようです。真の意図を見つける唯一の方法は、コードを書いた人に尋ねることです:)。
Mike Strobel、2010

ありがとう、マイク。私は元の開発者が私ではなくクレイジーな人であると疑い始めています。しかし、それは常にチェックするのに役立ちます;)
Andrew Russell

スレッドの安全性は、特にSIMD命令を使用しないプラットフォーム用のFP計算負荷の高いライブラリを作成する場合に、提供するのにコストのかかる保証になることがあります。
Jason Kozak

-1

360のGCは基本的に高価なGEN 2コレクションのみを実行するので、フレームごとに作成および削除される一時変数(tempオブジェクトなど)がコレクション全体を実行し、パフォーマンスが非常に速く停止します。

彼らがこの方法でそのオブジェクトを再利用し、オブジェクトを収集しないようにしたのではないかと思います。


1
これも私には起こりましたが、一時的なメンバーは値の型のように見えるので、マネージヒープに割り当てられません。
Mike Strobel、2010

2
そうですが、彼らがクラスのメンバーである場合に限ります。それらがローカル(メソッドスコープ)である場合、それらはスタックに割り当てられます。問題は、彼らが単にその道を進んでいなかった理由です。
Mike Strobel、

1
@Blair-MSDN(msdn.microsoft.com/en-us/library/bb203912.aspx)によると、Xbox360は.NET Compact Frameworkを使用しています。GCの違いが関係しているようですので、今後の調​​査のために追及したいと思います。
ローガンキンケイド

1
@ブレア:@ローガンキンケイドは正しいです。CFのガベージコレクターの動作は、通常のフレームワークの動作とは異なります。XNA Game Studio 3.0 Unleashedの話題については良い話がありますが、その本は4.0のリリースで間もなく時代遅れになります。
Steven Evers

1
@Blair:360(およびCF経由でターゲットとされるほとんどのデバイス)のメモリ環境は限られているため、Mark&Sweep GCが使用されます。その結果、多くの小さな割り当てがコレクションをトリガーし、コレクション時間は参照の数に比例します。詳細はこちら:download.microsoft.com
Jason Kozak
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.