C#のインライン関数?


276

C#で「インライン関数」をどのように実行しますか?私はその概念を理解していないと思います。彼らは匿名メソッドのようですか?ラムダ関数は好きですか?

:回答は、ほぼ完全に関数インライン化する機能を扱っています。つまり、「関数呼び出しサイトを呼び出し先の本文で置き換える手動またはコンパイラの最適化」です。匿名(別名ラムダ)関数に興味がある場合は、@ jalfの回答を参照してください。または、誰もが話し続けている「ラムダ」とは何ですか?


11
ようやく可能になりました-私の答えを見てください。
konrad.kruczynski 2012年

1
.NET 4.5より前の最も近いものについては、質問変数をラムダ関数として定義する方法を参照してください。実際にはインラインとしてコンパイルされていませんが、インライン宣言のような短い関数宣言です。インラインを使用して達成しようとしていることに依存します。
AppFzx 2013年

回答:


384

最後に.NET 4.5では、CLRにより、値を使用してインライン化する1つのメソッドをヒント/提案できMethodImplOptions.AggressiveInliningます。これは、Monoのトランクでも使用できます(現在コミットされています)。

// The full attribute usage is in mscorlib.dll,
// so should not need to include extra references
using System.Runtime.CompilerServices; 

...

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void MyMethod(...)

。以前は「力」が使用されていました。いくつかの反対投票があったので、用語を明確にするように努めます。コメントとドキュメントのように、The method should be inlined if possible.特にMono(開いている)を考慮すると、インライン化またはより一般的なもの(仮想関数など)を考慮すると、いくつかのモノ固有の技術的な制限があります。全体として、はい、これはコンパイラーへのヒントですが、私はそれが求められたものだと思います。


17
+1-フレームワークのバージョン要件についてより具体的になるように回答を更新しました。
M.バブコック

4
それはおそらくまだありませんインラインが、ジッタのヒューリスティックをオーバーライドすることは、ほとんどの状況で確かに十分です。
CodesInChaos

7
すべての.NETバージョンで機能する別のアプローチは、少し大きすぎるメソッドを2つのメソッドに分割することです。一方は他方を呼び出し、どちらも32バイトのILを超えません。正味の効果は、オリジナルがインライン化されたかのようです。
Rick Sladkey、2012

4
「インライン化」を強制するのではなく、JITと対話してプログラマが本当にここでインライン化を使用したいことを伝えようとするだけですが、JITが最後の言葉です。したがって、MSDN:可能であれば、メソッドをインライン化する必要があります。
Orel Eraki

11
比較すると、C ++のインライン提案は、コンパイラー固有のものでも、実際にはインライン化を強制しません。すべての関数をインライン化できるわけではありません(基本的に、再帰関数のようなものは難しいですが、他のケースもあります)。そう、そう、この「かなり」の力は典型的です。
Eamon Nerbonne 2013年

87

インラインメソッドは、関数のコードが呼び出し元にロールインされるコンパイラ最適化にすぎません。

C#でこれを行うためのメカニズムはなく、それらがサポートされている言語では控えめに使用する必要があります。どこかで使用する必要がある理由がわからない場合は、使用しないでください。

編集:明確にするために、それらを慎重に使用する必要がある2つの主な理由があります。

  1. 必要のない場合は、インラインを使用して大規模なバイナリを簡単に作成できます。
  2. コンパイラーは、パフォーマンスの観点から何かをインライン化する必要があるときに、あなたよりもよく知っている傾向があります

何もせずに、コンパイラーに処理を任せ、インライン化が最適なソリューションであるかどうかをプロファイルして把握するのが最善です。もちろん、インライン化することに意味のあるものもありますが(特に数学演算子)、コンパイラーにそれを処理させることは、通常、ベストプラクティスです。


35
通常、コンパイラーがインライン化を処理することは問題ないと思います。しかし、コンパイラーの決定をオーバーライドして、メソッドをインライン化するかどうかを指定したい場合があります。
mmmmmmmm

7
@Joel Coehoorn:モジュール化とアクセス保護を破壊するため、これは悪い習慣です。(プライベートメンバーにアクセスし、コードの別のポイントから呼び出されるクラスのインラインメソッドについて考えてみてください!)
mmmmmmmm

9
@Pomaインライン化を使用するのは奇妙な理由です。それが効果的であることを証明することは非常に疑わしい。
Chris Shouts

56
コンパイラが最もよく知っているという議論は間違っています。32 ILバイト、ピリオドを超えるメソッドはインライン化しません。これは、プロファイラーが特定したホットスポットがあり、私たちがまったく何もできないプロジェクト(私のものや、上記のEgorなど)にとっては恐ろしいものです。コードをカットアンドペーストして手動でインライン化する以外は何もありません。一言で言えば、あなたが本当にパフォーマンスを運転しているとき、これはひどい状況です。
明るい

6
インライン化は、見た目よりも微妙です。メモリには高速にアクセスできるキャッシュがあり、コードの現在の部分は変数と同じようにそれらのキャッシュに格納されます。命令の次のキャッシュラインをロードすることはキャッシュミスであり、単一の命令の10倍以上のコストがかかる可能性があります。最終的には、L2キャッシュにロードする必要があり、さらにコストがかかります。したがって、肥大化したコードは、より多くのキャッシュミスを引き起こす可能性があり、同じL1キャッシュラインに常駐するインライン関数は、キャッシュミスを減らし、コードを高速化する可能性があります。.Netを使用すると、さらに複雑になります。

56

アップデート:パーkonrad.kruczynskiの答えは、以下では、.NETのバージョンアップへと4.0を含むため、真です。

MethodImplAttributeクラスを使用して、メソッドがインライン化されないようにすることができます...

[MethodImpl(MethodImplOptions.NoInlining)]
void SomeMethod()
{
    // ...
}

...しかし、反対のことをして、それを強制的にインライン化する方法はありません。


2
それを知ることは興味深いですが、なぜメソッドがインライン化されるのを妨げるのでしょうか?モニターを見つめて少し時間を費やしましたが、インライン化が害を及ぼす理由を説明することはできません。
Camilo Martin

3
呼び出しスタック(つまりNullReferenceException)を受け取った場合、明らかにスタックの一番上のメソッドがそれをスローした方法はありません。もちろん、その呼び出しの1つが持っている可能性があります。しかし、どれ?
dzendras

19
GetExecutingAssemblyそしてGetCallingAssemblyメソッドがインライン化されているかどうかに応じて、異なる結果を与えることができます。メソッドをインライン化しないように強制すると、不確実性がなくなります。
stusmith 2012年

1
@Downvoter:私が書いた内容に同意できない場合は、理由を説明するコメントを投稿してください。それは一般的な礼儀であるだけでなく、合計20問以上の投票の投票で多くを達成することもできません。
BACON、2012

2
あなたの名前だけでも+1:Dに値するので、私が+2できることを望みます。
Retrodrone 2013年

33

あなたは2つの別々の概念を混同しています。関数のインライン化は、セマンティクスに影響を与えないコンパイラー最適化です。関数は、インライン化されているかどうかにかかわらず、同じように動作します。

一方、ラムダ関数は純粋に意味論的な概念です。言語仕様に定められた動作に従う限り、実装または実行方法に関する要件はありません。JITコンパイラーが適切であると感じる場合はインライン化でき、そうでない場合はインライン化できません。

C#にはインラインキーワードはありません。これは、特にJIT化された言語では、通常コンパイラーに任せることができる最適化であるためです。JITコンパイラーはランタイム統計にアクセスできるため、コードを記述する場合よりもはるかに効率的にインライン化するものを決定できます。コンパイラーが決定した場合、関数はインライン化されますが、どちらの方法でもそれを実行することはできません。:)


20
「関数は、インライン化されているかどうかにかかわらず、同じように動作します。」これが当てはまらないまれなケースがいくつかあります。つまり、スタックトレースについて知りたいロギング機能です。しかし、私はあなたのベースステートメントをあまり傷つけたくない:それは一般的に真実である。
Joel Coehoorn、2009年

「そして、どちらにしてもそれについてあなたができることは何もない。」-真実ではない。コンパイラへの「強力な提案」を何もしないと見なしていても、関数がインライン化されないようにすることができます。
BartoszKP 2017年

21

C ++の意味でのインライン関数ですか?通常の関数の内容は、コールサイトに自動的にインラインでコピーされますか?最終的には、関数の呼び出し時に関数呼び出しが実際には発生しません。

例:

inline int Add(int left, int right) { return left + right; }

もしそうなら、いいえ、これに相当するC#はありません。

または、別の関数内で宣言された関数を意味しますか?そうであれば、はい、C#は匿名メソッドまたはラムダ式を介してこれをサポートします。

例:

static void Example() {
  Func<int,int,int> add = (x,y) => x + y;
  var result = add(4,6);  // 10
}

21

コーディはそれを正しく理解していますが、インライン関数とは何かの例を提供したいと思います。

次のコードがあるとします。

private void OutputItem(string x)
{
    Console.WriteLine(x);

    //maybe encapsulate additional logic to decide 
    // whether to also write the message to Trace or a log file
}

public IList<string> BuildListAndOutput(IEnumerable<string> x)
{  // let's pretend IEnumerable<T>.ToList() doesn't exist for the moment
    IList<string> result = new List<string>();

    foreach(string y in x)
    {
        result.Add(y);
        OutputItem(y);
    }
    return result;
}

コンパイラジャスト・イン・タイム・オプティマイザは、この代わりのようなコードを書いていたかのようになるように、スタックにOutputItem()への呼び出しを配置し、繰り返しを避けるためにコードを変更することを選択することができます:

public IList<string> BuildListAndOutput(IEnumerable<string> x)
{
    IList<string> result = new List<string>();

    foreach(string y in x)
    {
        result.Add(y);

        // full OutputItem() implementation is placed here
        Console.WriteLine(y);   
    }

    return result;
}

この場合、OutputItem()関数がインライン化されたと言えます。他の場所からもOutputItem()が呼び出された場合でも、これが行われる場合があることに注意してください。

インライン化される可能性が高いシナリオを表示するように編集されました。


9
ただ明確にするために; インライン化を行うのはJITです。C#コンパイラではありません。
Marc Gravell

また、元々少なくとも、JITは、アセンブリの境界を越えても、静的メソッドをインライン化することを「好む」ことにも注意してください。したがって、従来の最適化手法では、メソッドを静的としてマークしていました。これは以来コミュニティーによって眉をひそめられてきましたが、今日まで、私は本質的に内部で非ポリモーフィックであり、通常、関連するスタックのfill + spillよりも実行コストが低いインラインメソッドを選択します。
Shaun Wilson

7

はいまさに、唯一の違いは、値を返すという事実です。

簡略化(式を使用しない):

List<T>.ForEach アクションを実行しますが、戻り結果を期待していません。

したがって、Action<T>デリゲートで十分です。

List<T>.ForEach(param => Console.WriteLine(param));

言うことと同じです:

List<T>.ForEach(delegate(T param) { Console.WriteLine(param); });

違いは、paramの型とデリゲートのdeclerationが使用法によって推論され、中かっこが単純なインラインメソッドで必要とされないことです。

どこに

List<T>.Where 結果を期待して関数を受け取ります。

だからFunction<T, bool>期待されます:

List<T>.Where(param => param.Value == SomeExpectedComparison);

これは次と同じです:

List<T>.Where(delegate(T param) { return param.Value == SomeExpectedComparison; });

これらのメソッドをインラインで宣言して、変数IEに割り当てることもできます。

Action myAction = () => Console.WriteLine("I'm doing something Nifty!");

myAction();

または

Function<object, string> myFunction = theObject => theObject.ToString();

string myString = myFunction(someObject);

これがお役に立てば幸いです。


2

コードを強制的にインライン化したい場合があります。

たとえば、非常に反復的なブロック内で多数の決定が行われる複雑なルーチンがあり、それらの決定の結果、実行されるアクションは類似しているがわずかに異なる場合などです。たとえば、複雑な(非DB駆動の)並べ替え比較演算子を考えます。並べ替えアルゴリズムは、高速言語の文法的および意味的基準に従って単語を並べ替えている場合など、関連のないさまざまな基準に従って要素を並べ替えます。認識システム。ソースコードの可読性とモジュール性を維持するために、これらのアクションを処理するヘルパー関数を作成する傾向があります。

これらのヘルパー関数はインライン化する必要があることを知っています。これは、人間が理解する必要がなかった場合にコードが記述される方法だからです。この場合、関数呼び出しのオーバーヘッドがないことを確認したいと思います。


2

「これらのものをそのままにして、コンパイラーに処理を任せるのが最善です」(Cody Brocious)の文は、完全に愚かです。私は20年間高性能ゲームコードをプログラミングしてきましたが、インライン化する必要があるコード(関数)かどうかを知るのに「十分にスマート」なコンパイラーにまだ出会っていません。C#に「インライン」ステートメントがあると便利ですが、実際には、コンパイラは、「インライン」ヒントなしで常にインライン化するかどうかを判断するために必要なすべての情報を持っているわけではありません。関数が小さい(アクセサ)場合は、自動的にインライン化される可能性がありますが、数行のコードの場合はどうでしょうか。ナンセンス、コンパイラは知る方法がなく、最適化されたコード(アルゴリズム以外)をコンパイラに任せることはできません。


3
JITerリアルタイム関数呼び出しのプロファイリングん「Nonesenseは、コンパイラが知る方法がない」..
ブライアン・ゴードン・

3
.NET JITは、コンパイル時に2パスの静的分析で可能なものよりも実行時に最適化されることが知られています。「古い」コーダーが把握するのが難しいのは、コンパイラーではなくJITがネイティブコード生成を担当することです。コンパイラーではなく、JITがインライン・メソッドを担当します。
Shaun Wilson

1
私がソースコードなしで、呼び出したコードをコンパイルすることは可能であり、JITは呼び出しをインライン化することを選択する場合があります。通常私は同意しますが、人間として、あなたはもはやツールを超えることができないと言います。
Shaun Wilson


0

いいえ、C#にはそのような構成はありませんが、.NET JITコンパイラーはJIT時間にインライン関数呼び出しを実行することを決定できます。しかし、実際にそのような最適化を行っているかどうかはわかりません。
(私はそれがすべきだと思います:-))


0

アセンブリがngen-edになる場合は、TargetedPatchingOptOutを確認することをお勧めします。これは、ngenがメソッドをインライン化するかどうかを決定するのに役立ちます。MSDNリファレンス

ただし、最適化するのは宣言的なヒントであり、命令的なコマンドではありません。


そして、JITは、インライン化するかどうかにかかわらず、とにかくずっとよく知っています。JITが呼び出しサイトを最適化しない場合、明らかに役に立たないでしょうが、それでは、なぜ数回しか呼び出されない関数の最小限のオーバーヘッドを心配する必要があるのでしょうか。
Voo

-7

ラムダ式はインライン関数です!C#には、インラインなどの追加の属性がないと思います。


9
私はあなたに反対票を投じませんでしたが、ランダムな回答を投稿する前に質問を読み、それらを理解していることを確認してください。en.wikipedia.org/wiki/Inline_function
Camilo Martin

1
この答えは、それが判明したほど悪くはありません。OPは「関数のインライン化」と「ラムダ関数」について明確ではありません。残念ながら、MSDNはラムダを「インラインステートメント」または「インラインコード」と呼んでいます。ただし、Konradの回答によると、メソッドのインライン化についてコンパイラーにヒントを与える属性があります
StuartLC 2015

-7

C#は、Pythonなどの動的言語のようにインラインメソッド(または関数)をサポートしていません。ただし、以下の例のように、包含メソッドの変数にアクセスする必要がある場合など、匿名メソッドとラムダを同様の目的に使用できます。

static void Main(string[] args)
{
    int a = 1;

    Action inline = () => a++;
    inline();
    //here a = 2
}

10
これはインライン関数で何をするのですか?これは匿名メソッドです。
Tomer W
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.