web-dev-qa-db-ja.com

C#で構造体を値で渡したり、refで渡したりすると、いつより効率的になりますか?

私は少し調べましたが、構造体は16バイト未満である必要があるという一般的な知恵によると、コピーしないとパフォーマンスが低下するためです。 C#7とrefリターンを使用すると、構造体を完全にコピーすることを完全に回避することが非常に簡単になりました。構造体のサイズが小さくなるにつれて、参照渡しでは、値をコピーするだけのオーバーヘッドが増えると思います。

値による構造体の受け渡しが参照によるよりも速くなる時期についての経験則はありますか?これに影響する要因は何ですか? (構造サイズ、プロセスビット数など)

より多くのコンテキスト

私は、キャッシュの使いやすさを最大にするために、データの大部分が構造体の連続した配列として表されるゲームに取り組んでいます。ご想像のとおり、構造体を渡すことは、このようなシナリオでは非常に一般的です。プロファイリングが何かのパフォーマンスへの影響を判断する唯一の実際の方法であることを私は知っています。ただし、その背後にある理論的な概念を理解し、その理解を念頭に置いてコードを記述し、エッジのケースのみをプロファイルしたいと思います。

また、私は参照によってすべてを渡すことのベストプラクティスや正気について尋ねているのではないことに注意してください。私は「ベストプラクティス」とその影響を認識しており、意図的にそれらに従わないことを選択します。

「重複」タグへの対処

値渡しのパフォーマンスとC#.NETでの参照渡しのパフォーマンス -この質問では、私が求めているものとは完全に異なるrefによる参照型の受け渡しについて説明します。

。Netでは、パフォーマンス上の理由で参照によって構造体を渡す必要がある場合はいつですか? -2番目の質問は主題に少し触れていますが、構造体の特定のサイズについてです。

Eric Lippertの記事 からの質問に答えるには:

本当にその質問に答える必要がありますか?はい、そうです。それは私がたくさんのコードを書く方法に影響するからです。

それは本当にボトルネックですか?おそらくそうではありません。しかし、それがプログラムの99%のデータアクセスパターンであるため、私はまだ知りたいです。私の考えでは、これは正しいデータ構造を選択することに似ています。

違いは関係ありますか?あります。参照による大きな構造体の受け渡しはより高速です。私はこれの限界を理解しようとしているだけです。

これはあなたが言う「より速い」とは何ですか?同じタスクでCPUに与える作業を少なくする場合と同じです。

全体像を見ていますか?はい。前に述べたように、それは私が全体を書く方法に影響を与えます。

さまざまな組み合わせを測定できることは知っています。そして、それは私に何を伝えますか?そのXは、[。NETバージョン、プロセスビット数、OS、CPU]の組み合わせでYよりも高速です。 Linuxはどうですか? Androidはどうですか? iOSはどうですか?すべての可能なハードウェア/ソフトウェアの組み合わせですべての順列をベンチマークする必要がありますか?

それは実行可能な戦略ではないと思います。したがって、CLR/JIT/ASM/CPUについてよく知っている誰かがそれがどのように機能するかを教えてくれれば、コードを書くときに情報に基づいた決定を下せるようになるとよいと思います。

私が探している答えは、構造サイズについての前述の16バイトガイドラインとその理由の説明に似ています。

36
loodakrawa

一般に、参照渡しは高速です。
構造体を参照で渡す場合、32/64ビット整数である構造体へのポインタのみを渡します。
構造体を値で渡す場合、構造体全体をコピーしてから、新しいコピーへのポインタを渡す必要があります。
構造体が非常に小さい場合、たとえばintの場合を除き、参照渡しの方が高速です。

また、値で渡すと、メモリの割り当てと割り当て解除のためにosへの呼び出しの数が増加します。これらの呼び出しは、osがレジストリで利用可能なスペースをチェックする必要があるため、時間がかかります。

6
GideonMax

構造体を参照渡しする場合は、どのようなサイズでも構いません。まだ8(x64を想定)バイトポインターを処理しています。最高のパフォーマンスを得るには、データドリブンデザインと呼ばれるCPUキャッシュ対応のデザインが必要です。

ゲームでは、エンティティコンポーネントシステムと呼ばれる特別なデータドリブンデザインがよく使用されます。 Konrad Kokosa Chapter 14による本Pro .NETメモリ管理を参照してください。

基本的な考え方は、ゲームエンティティを更新できるということです。 Movable、Car、Plane、...は、隣接する配列に格納されているすべてのエンティティの位置などの共通のプロパティを共有します。 1Kエンティティの位置をインクリメントする必要がある場合は、すべてのエンティティの位置配列の配列インデックスを検索し、そこで更新するだけです。これにより、最高のデータ局所性が提供されます。すべてがクラスに格納される場合、各クラスインスタンスの多くのthisポインタによってCPUプリフェッチャーが失われます。

いくつかのリファレンスアーキテクチャについては、このIntelの投稿を参照してください。 https://software.intel.com/en-us/articles/get-started-with-the-unity-entity-component-system-ecs-c-sharp -job-system-and-burst-compiler

Entity Component Systemsはたくさんありますが、これまでのところ、主な作業データ構造としてref構造体を使用しているものはありません。その理由は、すべての一般的なものがref構造体が導入されたC#7.2よりもはるかに長く存在しているためです。

3
Alois Kraus