Inline-Funktionen in C #?

276

Wie macht man "Inline-Funktionen" in C #? Ich glaube nicht, dass ich das Konzept verstehe. Sind sie wie anonyme Methoden? Wie Lambda-Funktionen?

Hinweis : Die Antworten beziehen sich fast ausschließlich auf die Fähigkeit, Funktionen zu integrieren , dh "eine manuelle oder Compiler-Optimierung, die eine Funktionsaufruf-Site durch den Körper des Angerufenen ersetzt". Wenn Sie an anonymen (auch bekannt als Lambda) Funktionen interessiert sind , lesen Sie die Antwort von @ jalf oder Was ist das für ein Lambda, von dem alle reden? .

Dinah
quelle
11
Es ist endlich möglich - siehe meine Antwort.
konrad.kruczynski
1
Für die nächste Sache vor .NET 4.5 siehe Frage Wie man eine Variable als Lambda-Funktion definiert . Es wird NICHT als Inline-Dekompilierung kompiliert, sondern ist eine kurze Funktionsdeklaration wie die Inline-Deklaration. Hängt davon ab, was Sie mit Inline erreichen möchten.
AppFzx
Wenn Sie neugierig sind, sehen Sie sich diese VS-Erweiterung an .
TripleAccretion

Antworten:

384

Schließlich in .NET 4.5 ermöglicht die CLR ein bis hint / vorschlagen 1 Methode inlining Verwendung MethodImplOptions.AggressiveInliningWert. Es ist auch im Kofferraum des Mono erhältlich (heute verpflichtet).

// 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(...)

1 . Bisher wurde hier "Kraft" angewendet. Da es einige Abstimmungen gab, werde ich versuchen, den Begriff zu klären. Wie in den Kommentaren und in der Dokumentation gibt es The method should be inlined if possible.insbesondere in Bezug auf Mono (das offen ist) einige monospezifische technische Einschränkungen in Bezug auf Inlining oder allgemeinere (wie virtuelle Funktionen). Insgesamt ja, dies ist ein Hinweis für den Compiler, aber ich denke, das wurde gefragt.

konrad.kruczynski
quelle
17
+1 - Ihre Antwort wurde aktualisiert, um die Anforderungen an die Framework-Version genauer zu definieren.
M.Babcock
4
Es ist wahrscheinlich immer noch keine Inline- Kraft , aber das Überschreiben der JITters-Heuristik ist in den meisten Situationen sicherlich ausreichend.
CodesInChaos
7
Ein anderer Ansatz, der mit allen .NET-Versionen funktionieren kann, besteht darin, eine etwas zu große Methode in zwei Methoden aufzuteilen, von denen eine die andere aufruft und von denen keine 32 Byte IL überschreitet. Der Nettoeffekt ist, als ob das Original inline wäre.
Rick Sladkey
4
Es erzwingt nicht das "Inlining", sondern versucht nur, mit der JIT zu sprechen und ihr zu sagen, dass der Programmierer Inlining hier wirklich verwenden möchte, aber die JIT hat das letzte Wort. Daher MSDN: Die Methode sollte nach Möglichkeit inline sein.
Orel Eraki
11
Im Vergleich dazu erzwingen die Inline-Vorschläge von C ++, auch die compilerspezifischen, keine Inline-Vorschläge: Nicht alle Funktionen können inline sein (grundsätzlich sind Dinge wie rekursive Funktionen schwierig, aber es gibt auch andere Fälle). Also ja, diese "nicht ganz" Kraft ist typisch.
Eamon Nerbonne
87

Inline-Methoden sind einfach eine Compiler-Optimierung, bei der der Code einer Funktion in den Aufrufer gerollt wird.

Es gibt keinen Mechanismus, mit dem dies in C # durchgeführt werden kann, und sie sollten sparsam in Sprachen verwendet werden, in denen sie unterstützt werden. Wenn Sie nicht wissen, warum sie irgendwo verwendet werden sollten, sollten sie es nicht sein.

Bearbeiten: Zur Verdeutlichung gibt es zwei Hauptgründe, warum sie sparsam verwendet werden müssen:

  1. Es ist einfach, massive Binärdateien zu erstellen, indem Inline verwendet wird, wenn dies nicht erforderlich ist
  2. Der Compiler weiß in der Regel besser als Sie, wann aus Sicht der Leistung etwas eingefügt werden sollte

Lassen Sie die Dinge am besten in Ruhe und lassen Sie den Compiler seine Arbeit erledigen. Profilieren Sie dann und finden Sie heraus, ob Inline die beste Lösung für Sie ist. Natürlich sind einige Dinge nur sinnvoll, um inline zu sein (insbesondere mathematische Operatoren), aber es ist normalerweise die beste Vorgehensweise, den Compiler damit umgehen zu lassen.

Cody Brocious
quelle
35
Normalerweise denke ich, dass es in Ordnung ist, dass der Compiler Inlining verarbeitet. Es gibt jedoch Situationen, in denen ich die Compilerentscheidung überschreiben und eine Methode inline oder nicht inline schalten möchte.
mmmmmmmm
7
@ Joel Coehoorn: Dies wäre eine schlechte Praxis, da dadurch die Modularisierung und der Zugriffsschutz unterbrochen würden. (Denken Sie an eine Inline-Methode in einer Klasse, die auf private Mitglieder zugreift und von einem anderen Punkt im Code aufgerufen wird!)
mmmmmmmm
9
@Poma Das ist ein seltsamer Grund, Inlining zu verwenden. Ich bezweifle sehr, dass es sich als effektiv erweisen würde.
Chris Shouts
56
Die Argumente, dass der Compiler am besten Bescheid weiß, sind einfach falsch. Es wird keine Methode mit mehr als 32 IL-Bytes (Punkt) inline. Dies ist schrecklich für Projekte (wie meine und auch die von Egor oben), die von Profilern identifizierte Hotspots haben, gegen die wir absolut nichts tun können. Nichts, außer Code ausschneiden und einfügen und so manuell inline. Mit einem Wort, dies ist ein schrecklicher Zustand, wenn Sie wirklich Leistung bringen.
hell
6
Inlining ist subtiler als es erscheint. Der Speicher verfügt über Caches, auf die schnell zugegriffen werden kann, und der aktuelle Teil des Codes wird genau wie Variablen in diesen Caches gespeichert. Das Laden der nächsten Cache-Befehlszeile ist ein Cache-Fehler und kostet möglicherweise das 10-fache oder mehr, was ein einzelner Befehl bewirkt. Schließlich muss es in den L2-Cache geladen werden, was sogar noch teurer ist. Aufgeblähter Code kann daher dazu führen, dass mehr Cache übersehen wird, wobei eine Inline-Funktion, die die ganze Zeit in denselben L1-Cache-Zeilen verbleibt, zu weniger Cache-Fehlern und möglicherweise schnellerem Code führen kann. Mit .Net ist es noch komplexer.
56

Update: Per konrad.kruczynski Antwort , folgendes gilt für Versionen von .NET bis einschließlich 4.0.

Sie können die MethodImplAttribute-Klasse verwenden , um zu verhindern , dass eine Methode inline wird ...

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

... aber es gibt keine Möglichkeit , das Gegenteil zu tun und zwingt es inlined werden.

SPECK
quelle
2
Interessant zu wissen, aber warum zum Teufel verhindern Sie, dass eine Methode eingebunden wird? Ich habe einige Zeit auf den Monitor gestarrt, aber ich kann mir keinen Grund ausdenken, warum Inlining Schaden anrichten könnte.
Camilo Martin
3
Wenn Sie einen Aufrufstapel erhalten (dh NullReferenceException) und es offensichtlich keine Möglichkeit gibt, dass die Methode oben auf dem Stapel ihn geworfen hat. Natürlich kann einer seiner Anrufe haben. Aber welcher?
Dzendras
19
GetExecutingAssemblyund GetCallingAssemblykann unterschiedliche Ergebnisse liefern, abhängig davon, ob die Methode inline ist. Das Erzwingen, dass eine Methode nicht inline ist, beseitigt jegliche Unsicherheit.
Stusmith
1
@Downvoter: Wenn Sie mit dem, was ich geschrieben habe, nicht einverstanden sind, sollten Sie einen Kommentar veröffentlichen, in dem erklärt wird, warum. Es ist nicht nur übliche Höflichkeit, Sie erreichen auch nicht viel, indem Sie die Stimmenzahl auf über 20 Antworten reduzieren.
Speck
2
Ich wünschte, ich könnte +2, weil nur dein Name +1 verdient: D.
Retrodrone
33

Sie mischen zwei verschiedene Konzepte. Function Inlining ist eine Compileroptimierung, die keinen Einfluss auf die Semantik hat. Eine Funktion verhält sich unabhängig davon, ob sie inline ist oder nicht.

Andererseits sind Lambda-Funktionen ein rein semantisches Konzept. Es gibt keine Anforderung, wie sie implementiert oder ausgeführt werden sollen, solange sie dem in der Sprachspezifikation festgelegten Verhalten folgen. Sie können eingefügt werden, wenn der JIT-Compiler Lust dazu hat oder nicht, wenn dies nicht der Fall ist.

In C # gibt es kein Inline-Schlüsselwort, da es sich um eine Optimierung handelt, die normalerweise dem Compiler überlassen werden kann, insbesondere in JIT-Sprachen. Der JIT-Compiler hat Zugriff auf Laufzeitstatistiken, mit denen er wesentlich effizienter entscheiden kann, was beim Schreiben des Codes inline sein soll. Eine Funktion wird eingefügt, wenn der Compiler dies wünscht, und Sie können in beiden Fällen nichts dagegen tun. :) :)

jalf
quelle
20
"Eine Funktion verhält sich gleich, unabhängig davon, ob sie inline ist oder nicht." Es gibt einige seltene Fälle, in denen dies nicht zutrifft: Protokollierungsfunktionen, die Informationen zum Stack-Trace erhalten möchten. Aber ich möchte Ihre Basisaussage nicht zu sehr untergraben: Es ist im Allgemeinen wahr.
Joel Coehoorn
"und es gibt nichts, was du dagegen tun kannst." - nicht wahr. Auch wenn wir den Compiler nicht als "starken Vorschlag" betrachten, können Sie verhindern, dass Funktionen eingebunden werden.
BartoszKP
21

Meinen Sie Inline-Funktionen im C ++ - Sinne? In welcher Form wird der Inhalt einer normalen Funktion automatisch inline auf die Call-Site kopiert? Der Endeffekt ist, dass beim Aufrufen einer Funktion tatsächlich kein Funktionsaufruf erfolgt.

Beispiel:

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

Wenn ja, dann nein, es gibt kein C # -Äquivalent dazu.

Oder meinen Sie Funktionen, die in einer anderen Funktion deklariert sind? Wenn ja, dann unterstützt C # dies über anonyme Methoden oder Lambda-Ausdrücke.

Beispiel:

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

Cody hat es richtig gemacht, aber ich möchte ein Beispiel dafür geben, was eine Inline-Funktion ist.

Angenommen, Sie haben diesen Code:

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;
}

Das Just-In-Time-Optimierungsprogramm des Compilers kann den Code ändern, um zu vermeiden, dass OutputItem () wiederholt auf dem Stapel aufgerufen wird, sodass es so aussieht, als hätten Sie den Code stattdessen wie folgt geschrieben:

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;
}

In diesem Fall würden wir sagen, dass die Funktion OutputItem () inline ist. Beachten Sie, dass dies auch dann möglich ist, wenn OutputItem () auch von anderen Stellen aus aufgerufen wird.

Bearbeitet, um ein Szenario anzuzeigen, das mit größerer Wahrscheinlichkeit inline ist.

Joel Coehoorn
quelle
9
Nur um es zu klären; es ist die JIT, die das Inlining macht; nicht der C # -Compiler.
Marc Gravell
Beachten Sie auch, dass die JIT ursprünglich zumindest statische Methoden bevorzugen würde, selbst über Baugruppengrenzen hinweg. Daher bestand eine Technik der alten Schule zur Optimierung darin, Ihre Methoden als statisch zu markieren. Dies wurde seitdem von der Community verpönt, aber bis heute werde ich mich für Inline-Methoden entscheiden, die intern und nicht polymorph sind und in der Regel weniger kosten als die zugehörige Stapelfüllung + Verschüttung.
Shaun Wilson
7

Ja Genau, der einzige Unterschied ist die Tatsache, dass ein Wert zurückgegeben wird.

Vereinfachung (ohne Ausdrücke):

List<T>.ForEach Führt eine Aktion aus, erwartet kein Rückgabeergebnis.

Ein Action<T>Delegierter würde also ausreichen. Sagen Sie:

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

ist das gleiche wie zu sagen:

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

Der Unterschied besteht darin, dass der Parametertyp und die Delegiertendekleration durch die Verwendung abgeleitet werden und die geschweiften Klammern bei einer einfachen Inline-Methode nicht erforderlich sind.

Wohingegen

List<T>.Where Übernimmt eine Funktion und erwartet ein Ergebnis.

Also Function<T, bool>wäre ein zu erwarten:

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

Das ist das gleiche wie:

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

Sie können diese Methoden auch inline deklarieren und sie den Variablen IE zuordnen:

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

myAction();

oder

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

string myString = myFunction(someObject);

Ich hoffe das hilft.

Quintin Robinson
quelle
2

Es gibt Fälle, in denen ich die Inline-Eingabe von Code erzwingen möchte.

Zum Beispiel, wenn ich eine komplexe Routine habe, in der eine große Anzahl von Entscheidungen innerhalb eines stark iterativen Blocks getroffen wird und diese Entscheidungen zu ähnlichen, aber leicht unterschiedlichen Aktionen führen, die ausgeführt werden müssen. Stellen Sie sich zum Beispiel einen komplexen (nicht DB-gesteuerten) Sortiervergleich vor, bei dem der Sortieralgorithmus die Elemente nach einer Reihe verschiedener nicht verwandter Kriterien sortiert, wie dies beispielsweise der Fall wäre, wenn Wörter nach gramatischen und semantischen Kriterien für eine schnelle Sprache sortiert würden Erkennungssystem. Ich würde dazu neigen, Hilfsfunktionen zu schreiben, um diese Aktionen auszuführen, um die Lesbarkeit und Modularität des Quellcodes aufrechtzuerhalten.

Ich weiß, dass diese Hilfsfunktionen eingebunden sein sollten, da der Code auf diese Weise geschrieben würde, wenn er niemals von einem Menschen verstanden werden müsste. Ich würde in diesem Fall sicherlich sicherstellen wollen, dass es keinen Funktionsaufruf-Overhead gibt.

Entwickler
quelle
2

Die Aussage "Es ist am besten, diese Dinge in Ruhe zu lassen und den Compiler die Arbeit machen zu lassen." (Cody Brocious) ist völliger Quatsch. Ich programmiere seit 20 Jahren Hochleistungsspielcode und habe noch keinen Compiler gefunden, der "klug genug" ist, um zu wissen, welcher Code eingebunden werden soll (Funktionen) oder nicht. Es wäre nützlich, eine "Inline" -Anweisung in c # zu haben. Die Wahrheit ist, dass der Compiler nicht über alle Informationen verfügt, die er benötigt, um zu bestimmen, welche Funktion ohne den "Inline" -Hinweis immer inline oder nicht eingefügt werden soll. Sicher, wenn die Funktion klein ist (Accessor), wird sie möglicherweise automatisch eingefügt, aber was ist, wenn es sich um ein paar Codezeilen handelt? Unsinnig, der Compiler hat keine Möglichkeit zu wissen, Sie können dies nicht einfach dem Compiler überlassen, um optimierten Code (über Algorithmen hinaus) zu erhalten.

gvick2002
quelle
3
"Unsinn, der Compiler hat keine Möglichkeit zu wissen" Der JITer führt eine Echtzeit-Profilerstellung von Funktionsaufrufen durch.
Brian Gordon
3
Es ist bekannt, dass die .NET-JIT zur Laufzeit besser optimiert als dies mit einer statischen Analyse in zwei Durchgängen zur Kompilierungszeit möglich ist. Für "Old School" -Codierer ist es schwierig zu verstehen, dass die JIT und nicht der Compiler für die native Codegenerierung verantwortlich ist. Die JIT, nicht der Compiler, ist für Inline-Methoden verantwortlich.
Shaun Wilson
1
Es ist mir möglich, Code, in den Sie aufrufen, ohne meinen Quellcode zu kompilieren, und die JIT kann sich dafür entscheiden, den Aufruf zu inline. Normalerweise würde ich zustimmen, aber ich würde sagen, dass Sie als Mensch die Werkzeuge nicht mehr übertreffen können.
Shaun Wilson
0

Nein, es gibt kein solches Konstrukt in C #, aber der .NET JIT-Compiler könnte entscheiden, Inline-Funktionsaufrufe zur JIT-Zeit durchzuführen. Aber ich weiß eigentlich nicht, ob es wirklich solche Optimierungen macht.
(Ich denke es sollte :-))

mmmmmmmm
quelle
0

Für den Fall, dass Ihre Assemblys erstellt werden, sollten Sie sich TargetedPatchingOptOut ansehen. Dies hilft ngen bei der Entscheidung, ob Inline-Methoden verwendet werden sollen. MSDN-Referenz

Es ist jedoch immer noch nur ein deklarativer Hinweis zur Optimierung, kein zwingender Befehl.

Tim Lovell-Smith
quelle
Und die JIT wird sowieso viel besser wissen, ob sie inline ist oder nicht. Natürlich hilft es Ihnen nicht, wenn die JIT die Call-Site nicht optimiert, aber warum zum Teufel sollte ich mir dann Gedanken über den minimalen Overhead einer Funktion machen, die nur einige Male aufgerufen wird?
Voo
-7

Lambda-Ausdrücke sind Inline-Funktionen! Ich denke, dass C # kein zusätzliches Attribut wie Inline oder ähnliches hat!

cordellcp3
quelle
9
Ich habe Sie nicht abgelehnt, aber bitte lesen Sie die Fragen und stellen Sie sicher, dass Sie sie verstehen, bevor Sie eine zufällige Antwort veröffentlichen. en.wikipedia.org/wiki/Inline_function
Camilo Martin
1
Diese Antwort ist nicht so schlecht, wie es sich herausgestellt hat - das OP ist unklar über "Function Inlining" gegenüber "Lambda-Funktionen", und MSDN bezeichnet Lambdas leider als "Inline-Anweisungen" oder "Inline-Code" . Gemäß der Antwort von Konrad gibt es jedoch ein Attribut , das dem Compiler einen Hinweis auf das Inlining einer Methode gibt.
StuartLC
-7

C # unterstützt keine Inline-Methoden (oder -Funktionen) wie dynamische Sprachen wie Python. Anonyme Methoden und Lambdas können jedoch für ähnliche Zwecke verwendet werden, z. B. wenn Sie wie im folgenden Beispiel auf eine Variable in der enthaltenen Methode zugreifen müssen.

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

    Action inline = () => a++;
    inline();
    //here a = 2
}
Yordan Pavlov
quelle
10
Was hat das mit Inline-Funktionen zu tun? Dies ist eine anonyme Methode.
Tomer W