C#で「in」パラメータ修飾子を使用するのはなぜですか?


86

だから、私は(私が思うに)inパラメータ修飾子が何をするのか理解しています。しかし、それが行うことはかなり冗長であるように見えます。

通常、aを使用する唯一の理由refは、呼び出し変数を変更することであると思いますin。これは、によって明示的に禁止されています。したがって、in参照による受け渡しは、値による受け渡しと論理的に同等のようです。

パフォーマンス上の利点はありますか?物事のバックエンド側では、refパラメーターは少なくとも変数の物理アドレスをコピーする必要があると私は信じていました。これは、一般的なオブジェクト参照と同じサイズである必要があります。

それでは、大きな構造体だけに利点があるのでしょうか、それとも他の場所で魅力的なものにする舞台裏のコンパイラ最適化があるのでしょうか。後者の場合、なぜすべてのパラメーターをaにするべきではないのinですか?


1
はい、パフォーマンス上の利点があります。構造体をコピーする代わりに、参照によって構造体refを渡すために使用されます。構造体を変更してはならないことを意味します。in
PanagiotisKanavos18年

@dbcいいえ、これは相互運用とは何の関係もありません
PanagiotisKanavos18年

5
値型のパフォーマンス。C#7.2の新しい「in」キーワード
Silvermind 2018年

1
詳細はこちら。最後の警告に注意してください:It means that you should never pass a non-readonly struct as in parameter.
PanagiotisKanavos18年

これは重複していると誓うことができましたが、もう見つかりません
JAD 2018年

回答:


81

in 最近、C#言語が導入されました。

in実際にはref readonlyです。一般的に言って、in役立つユースケースは1つだけですreadonly struct。それは、多数の大きなを処理する高性能アプリです。

あなたが持っていると仮定して:

readonly struct VeryLarge
{
    public readonly long Value1;   
    public readonly long Value2;

    public long Compute() { }
    // etc
}

そして

void Process(in VeryLarge value) { }

その場合、メソッドでVeryLargeこの構造体を使用するProcessとき(呼び出し時などvalue.Compute())、構造体は防御コピーを作成せずに参照によって渡され、構造体の不変性はコンパイラによって保証されます。

修飾子を指定structして非読み取り専用を渡すinと、コンパイラがstructのメソッドを呼び出して上記のメソッドのプロパティにアクセスするときに防御コピー作成し、Processパフォーマンスに悪影響を与えることに注意してください。

本当に良いMSDNブログエントリがあり、注意深く読むことをお勧めします。

in-introducingの歴史的背景をもっと知りたい場合は、C#言語のGitHubリポジトリでこのディスカッションを読むことができます。

一般に、ほとんどの開発者は、導入がin間違いと見なされる可能性があることに同意しています。これはかなりエキゾチックな言語機能であり、高性能のエッジケースでのみ役立ちます。


それは、コンパイラが生成した守備のコピーを抑制し、あるいは単にプログラマが手動でそれ以外の場合は使用する状況で守備のコピーを作成する必要性なくすんref可能にすることにより、例えばsomeProc(in thing.someProperty);対をpropType myProp = thing.someProperty; someProc(ref myProp);?のinようなC#のみの概念outですか、それとも.NET Frameworkに追加されていますか?
スーパーキャット2018年

@supercat、は特別な属性を効果的に持つinため、を使用してプロパティを渡すことはできません。したがって、最初のスニペットはコンパイルされません。@VisualMelon、そうです、防御的なコピーは、メソッドを呼び出すか、構造体を引数として取得するメソッド内から構造体のプロパティにアクセスするときに発生します。inref
dymanoid

@dymanoidスパムを送信して申し訳ありませんが、あなたが書いたものを理解するのに約10回の試行が必要でした(そしてそれがあなたのせいではないと思います!)
VisualMelon18年

4
@dymanoid:両方inout(メソッドおよびプロパティは、それらがmodifiyかどうかを示すことができるれる手段と共に先頭からのフレームワークにされているべきである概念ですthis)。コンパイラーは、プロパティをin保持している一時への参照を渡すことにより、プロパティを引数に渡すことができます。別の言語で書かれた関数がその一時的なものを変更する場合、セマンティクスは少し厄介ですが、それは関数の動作がそのシグネチャと一致しないという欠点になります。
スーパーキャット2018年

1
@supercat、ここでディスカッションを開かないでください(とにかく、私は.NET Frameworkコンセプトチームに所属していません)。回答には長い議論があり、GitHubの議論は他のいくつかを参照しています-興味深い読み物です。ところで、VB.NETでは-refによってプロパティを渡すことができます-コンパイラはそれらの一時的なものを作成します(もちろん、あいまいな問題につながる可能性があります)。
dymanoid

42

in参照による受け渡しは、値による受け渡しと論理的に同等のようです。

正しい。

パフォーマンス上の利点はありますか?

はい。

物事のバックエンド側では、refパラメーターは少なくとも変数の物理アドレスをコピーする必要があると私は信じていました。これは、一般的なオブジェクト参照と同じサイズである必要があります。

存在しない要求オブジェクトへの参照と変数への参照の両方が同じサイズであることが、そこではない要件のいずれかが機械語のサイズであることは、しかし、はい、実際には32で32ビットであるの両方ビットマシンと64ビットマシンの64ビット。

「物理アドレス」がそれと何の関係があると思うかは私にはわかりません。Windowsでは、ユーザーモードコードの物理アドレスではなく、仮想アドレスを使用します。どのような状況で、物理アドレスがC#プログラムで意味があると思いますか?知りたいです。

また、ストレージの仮想アドレスとして、あらゆる種類の参照を実装する必要はありません。CLI仕様の準拠実装では、参照はGCテーブルへの不透明なハンドルである可能性があります。

大きな構造体だけに利点がありますか?

より大きな構造体を渡すコストを削減することが、この機能の動機付けとなるシナリオです。

inプログラムが実際に速くなるという保証はなく、プログラムが遅くなる可能性があることに注意してください。 パフォーマンスに関するすべての質問には、実証研究によって回答する必要があります常に勝つ最適化はほとんどありません。これは「常に勝つ」最適化ではありません。

他の場所で魅力的なものにする舞台裏のコンパイラ最適化はありますか?

コンパイラとランタイムがされている許可C#の仕様の規則に違反しないようにすれば、彼らが選択した任意の最適化を行うこと。私の知る限り、inパラメータのそのような最適化はまだありませんが、それは将来そのような最適化を排除するものではありません。

すべてのパラメータを入力しないのはなぜですか?

さて、intパラメータの代わりにin intパラメータを作成したとします。どのような費用がかかりますか?

  • コールサイトでは、ではなく変数が必要になりました
  • 変数を登録できません。ジッタの注意深く調整されたレジスタ割り当てスキームには、レンチが投入されました。
  • 呼び出しサイトのコードは、変数への参照を取得してスタックに配置する必要があるため、より大きくなりますが、その前に、値を呼び出しスタックにプッシュするだけで済みます。
  • コードが大きくなると、一部の短いジャンプ命令が長いジャンプ命令になっている可能性があるため、コードも大きくなります。これは、あらゆる種類のものにノックオン効果をもたらします。キャッシュはすぐにいっぱいになり、ジッターはより多くの作業を行う必要があり、ジッターはより大きなコードサイズで特定の最適化を行わないことを選択する場合があります。
  • 呼び出し先サイトでは、スタック(またはレジスター)上の値へのアクセスをポインターへの間接参照に変えました。現在、そのポインタはキャッシュ内にある可能性が高いですが、それでも、値への1命令アクセスを2命令アクセスに変更しました。
  • 等々。

double、をに変更するとしますin double。この場合も、変数を高性能浮動小数点レジスタに登録することはできません。これはパフォーマンスに影響を与えるだけでなく、プログラムの動作を変える可能性もあります。C#は、64ビットよりも高い精度で浮動小数点演算を実行できます。通常は、浮動小数点を登録できる場合にのみ実行します。

これは無料の最適化ではありません。代替案に対するパフォーマンスを測定する必要があります。設計ガイドラインが示唆しているように、最初から大きな構造体を作成しないことが最善の策です。


「どのような状況下で、物理アドレスがC#プログラムで意味があると思いますか[...]。」。C#は通常、メモリマップドI / Oや、リセットベクトルなどを備えたシステムで使用されます。ここでは、昔ながらのWintelボックスについて考えています。x86プロセッサとVGAフレームバッファを使用。さて、通常、これらを直接操作することはありませんが、可能ですよね?
OmarL 2018年

5
in通過することは、すべての場合に値で渡すことと本当に同等ですか?我々が持っている場合f(ref T x, in T y)f修正x、それがに同じ変更守るべきyとして呼び出されたときにf(ref a,a)。とが呼び出されたときに変更されるデリゲートfがある場合にも同じことが当てはまります。がないと、値が変更されることはないため、セマンティクスは異なります。コピーになるためです。in T yyiny
chi

2
OPは、「メモリの場所を説明するビットパターン」として、「オブジェクトの場所を説明するためにバックエンドが使用することを選択したもの」とは対照的に、非公式な方法で「物理アドレス」を意味していると思います。
sneftel 2018年

@ウィルソン:あなたが話していることの例を見てみたいです。C#でそのようなことをしようとしている人は誰も知りません。「システムC#」では、高レベルの管理言語を使用して低レベルのシステムコンポーネントを記述できるという話が何年にもわたってありましたが、実際にそのようなコードを見たことがありません。
EricLippert18年

2
@chi:その通りです。可変エイリアシングで危険なゲームをプレイすると、問題が発生する可能性があります。あなたがそれをするときにそれが痛いなら、それをしないでください。の目的in、「参照渡し、読み取り専用」の概念を表すことです。これは、賢明な行動をとるときに値を渡すことと論理的に同等です
EricLippert18年

6

有る。を渡す場合structinキーワードを使用すると、メソッドがコンテンツを変更するリスクなしに、コンパイラがポインタを渡すだけでよい最適化が可能になります。最後は重要です—コピー操作を回避します。大きな構造体では、これは違いの世界を作ることができます。


2
これは、質問がすでに言っていることを繰り返しているだけであり、実際の質問には答えていません。
Servy 2018年

読み取り専用構造体のみ。それ以外の場合、コンパイラは防御コピーを作成します
PanagiotisKanavos18年

1
実は違う。パフォーマンス上のメリットがあることを確認しています。INが言語を介したパフォーマンスで作成されたことを考えると、そうです、それが理由です。これにより、最適化が可能になります(読み取り専用構造体)。
お知らせメールTomTom

3
@TomTom Again、これ 、質問はすでにカバーしています。それが尋ねるのは、「それでは、より大きな構造体だけに利点があるのか​​、それとも他の場所で魅力的なものにする舞台裏のコンパイラ最適化があるのか​​?後者の場合、なぜすべてのパラメータをインにすべきではないのか?」それが実際に大きな構造体に有益であるかどうか、またはそれがまったく有益であるかどうかを尋ねていないことに注意してください。それはだただ、それは小さな構造体のために有益ですかどうかを尋ねる([はいあればフォローアップ)。あなたはどちらの質問にも答えていません。
Servy 2018年

2

これは、関数型プログラミングアプローチのために行われます。主要な原則の1つは、関数に副作用がないことです。つまり、パラメーターの値を変更してはならず、何らかの値を返す必要があります。C#では、値の変更を可能にする参照によってのみコピーされることなく、構造体(および値型)を渡す方法はありませんでした。迅速に、メソッドがその値を変更し始める限り、構造体をコピーするハッキーアルゴリズムがあります(それらのコレクションは構造体BTWです)。迅速に使用する人々は、すべてがコピーのものに気付いているわけではありません。これは、メモリ効率が高く明示的であるため、優れたc#機能です。あなたが何が新しいかを見ればスタック内の構造体と配列の周りでますます多くのことが行われていることがわかります。そして、ステートメントでは、これらの機能に必要なだけです。他の回答で言及されている制限がありますが、.netがどこに向かっているのかを理解するためにそれほど重要ではありません。


2

でc#7.2の読み取り専用リファレンスです

これは、refのようにオブジェクト全体を関数スタックに渡さないことを意味しますあなたは構造体への参照のみを渡す場合

ただし、オブジェクトの値を変更しようとすると、コンパイラエラーが発生します。

はい。これにより、大きな構造を使用する場合にコードのパフォーマンスを最適化できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.