Ist „call site“ ein vom Compiler generierter Auto-Code? Ich stoße häufig auf diesen Begriff und es klingt so, als würde die aufrufende Methode einfach als „call site“ bezeichnet. – was buchstäblich gut klingt, aber ich glaube, es hat einige tiefere Feinheiten. Und wenn „Call Site“ vom Compiler generierter Code ist – unter welchen Umständen ist dies erforderlich?
Ich spreche zwar im Kontext von C # .Net, aber dies scheint eine Standardterminologie zu sein. Ich würde mich über eine klare / präzise Erklärung mit einigen Beispielen freuen.
Kommentare
- Sowohl im Quellcode als auch im Auto gibt es den Begriff der Anrufstelle -generierter Objektcode.
- Möglicherweise möchten Sie untersuchen, wie Anrufseiten für eine dynamische Operation in C # 4 und höher generiert werden. Sie sind sehr interessant.
- @EricLippert Danke. Zum Glück habe ich den Link gefunden, auf dem Sie ihn sehr schön beschrieben haben. Ich konnte jedoch kein Blog von Ihnen finden, das sich speziell mit der Generierung von Anrufseiten befasst.
Antwort
„Anrufseite ”Bezieht sich auf den Ort, an dem eine Funktion oder Methode aufgerufen wird. Dies ist ein gebräuchlicher Begriff in der Compilerkonstruktion und nicht spezifisch für C #.
foo(); // call site void foo() { ... } // function definition
Die Aufrufsite ist kein speziell generierter Code. Am Aufrufort gibt der Compiler jedoch Anweisungen zum Aufrufen der Funktion aus. In der Regel sind dies:
- Speichern Sie den aktuellen Kontext in einem Stapelrahmen.
- Speichern Sie alle Argumente gemäß der Aufrufkonvention der Zielfunktion.
- Rufen Sie die Zielfunktion auf, für die möglicherweise ein Methodenversand erforderlich ist.
- Ruft den Rückgabewert gemäß der aufrufenden Konvention ab.
- Stellen Sie den Kontext aus dem Stapelrahmen wieder her.
Für den Methodenversand ist möglicherweise komplizierter Code erforderlich, um die Funktion zu finden, die tatsächlich aufgerufen werden soll. Dies ist erforderlich, wenn virtual
-Methoden aufgerufen werden, insbesondere Methoden über eine Schnittstelle. Das vollständige Verfahren zum Versenden von Methoden kann ziemlich kostspielig sein (häufig O (n) bei der Anzahl der implementierten Schnittstellen oder O (n) bei der Anzahl der verfügbaren Methoden). In den meisten Fällen (normalerweise 70–90%) werden auf einer Anrufsite Objekte desselben Typs angezeigt. Es ist dann sinnvoll, das Ergebnis der Methodensuche zwischenzuspeichern. Der Compiler kann dann an der Aufrufstelle folgenden Code generieren:
static lastType = null; static lastMethod = null; if (type(instance) != lastType) { lastType = type(instance); lastMethod = performMethodLookup(instance, methodName); } result = lastMethod(instance, args);
Dies wird als monomorphes bezeichnet Inline-Cache . Die .NET CLR entwickelte einen etwas komplizierteren Ansatz namens Virtual Stub Dispatch . Anstatt den Versand am Aufrufstandort durchzuführen, kompiliert die Laufzeit-JIT-Stub-Methoden. Es gibt verschiedene Stubs, um den vollständigen Methodenversand durchzuführen und eine zwischengespeicherte Methode zu verwenden. An der Anrufstelle haben wir dann nur einen Anruf an den Stub, und der Stub leitet den Anruf an das tatsächliche Ziel weiter. Abhängig von den Statistiken für diese Anrufstelle wird der Maschinencode an der Anrufstelle gepatcht, um einen anderen Stub zu verwenden.
Antwort
Die typischste Verwendung der Anrufseite ist wahrscheinlich die , wie sie derzeit auf Wikipedia deklariert ist : Es ist nichts weiter als die Stelle in einem Code, an der ein Anruf bei a erfolgt Funktion oder Unterprogramm wird erstellt. Es ist ein ganz allgemeiner Begriff, der auf alle Arten von Sprachen angewendet werden kann (und sicherlich nichts mit dynamischer Programmierung zu tun hat!).
Ich höre normalerweise den verwendeten Begriff (im Gegensatz zu nur dem Begriff „Aufruf“), um zu verdeutlichen, dass die Syntax und Semantik des Codes diskutiert wird, in dem eine Funktion / Methode / Unterroutine aufgerufen wird, im Gegensatz zur zugrunde liegenden Semantik des Aufrufs oder der Semantik bei der Definition von Funktion / Methode: in Auf diese Weise geht es absolut nicht um den Compiler oder irgendeinen generierten Code (obwohl der Begriff immer noch für den vom Compiler / zur Laufzeit generierten Code gilt, wie ich denke, amon bereits besprochen hat), ist es Es geht darum, den spezifischen Code, der in einen Aufruf übersetzt wird, in den Kontext zu bringen, und manchmal den Code in unmittelbarer Nähe.
Dies ist die Art von Bild, die ich in meinem Kopf habe:
call-site call call-target/method definition --> --> int.TryParse(str, out i); (runtime) public static bool TryPars(...
Die“ Call-Site „ist ein nützlicher Begriff, um über die Syntax eines Anrufs und die Semantik zu sprechen, die sich auf den Anrufer auswirkt (detailliert von amon). Stellen Sie sich zum Beispiel den neuen Modifikator in
in C # vor, einem „Aufruf“, der ein Argument als „in“ übergibt, ist eine bestimmte Semantik zugeordnet (er wird möglicherweise später von ref übergeben eine Kopie), aber dies ist nicht unbedingt aus der Syntax „am Aufrufort“ ersichtlich, wobei in
nicht obligatorisch ist. Die Idee ist, dass die Call-Site-Semantik (dh das Verhalten, das den Aufrufer / die Call-Site beeinflusst), ein Argument als Referenz mit in
zu übergeben, nahe beieinander liegt genug für diejenigen, die einen Wert übergeben, dass es nicht durch die Syntax an der Aufrufstelle eindeutig sein muss.Ich bin mit dieser Behauptung nicht einverstanden, und es würde mir schwer fallen, sie ohne einen Begriff wie „Call-Site“ zu diskutieren!
Ein weiteres Beispiel: Ein dynamischer Aufruf (z. B. für eine virtuelle / Schnittstellenmethode) hat etwas kompliziertes Laufzeitverhalten (wieder detailliert von amon) und es ist nicht bekannt, welche Methode zur Kompilierungszeit aufgerufen wird, aber alles, was die „Aufruf-Site“ interessiert, ist die Semantik des Versands dieses Aufrufs: Wenn Sie ToString()
auf einer object
ist es Ihnen nicht wirklich wichtig, dass es sich tatsächlich um eine string
an der Aufrufstelle; es ist Ihnen nur wichtig, dass object
eine virtuelle ToString()
-Methode verfügbar macht (es liegt an der Laufzeit um herauszufinden, welche Methode tatsächlich aufgerufen werden soll). In der Tat ist an der Anrufstelle (z. B. in C #) aus der Syntax nicht unbedingt ersichtlich, ob es sich um einen virtuellen Anruf handelt: Die Unterscheidung zwischen einem virtuellen und einem nicht virtuellen Anruf wird beim Anruf häufig als unwichtig genug angesehen. site , dass es für den Programmierer an der Aufruf-Site nicht eindeutig sein muss (obwohl es für den Compiler / die Laufzeit von entscheidender Bedeutung ist, einen aussagekräftigen Aufruf zu tätigen)
Ein letztes Beispiel: Betrachten Sie C # „s ref
und C ++“ s &
: die Semantik an der Aufrufstelle in beiden Die Sprache ist in beiden Sprachen ungefähr gleich: Eine Referenz wird anstelle eines Werts übergeben, aber jede Sprache hat an der Aufrufstelle eine andere Syntax, wobei C # ref
erfordert und C ++ nicht erfordern &
(oder so ähnlich). Auch hier haben die Sprachdesigner einige Syntaxentscheidungen darüber getroffen, wie Anrufe an der Anrufstelle dargestellt werden, die durch die Semantik an der Anrufstelle informiert werden. (Ich bevorzuge die C # -Anrufsite-Syntax, da sie die Call-Site-Semantik offenlegt, die ich als Programmierer meines Erachtens bestätigen muss, wenn ich einen solchen Aufruf tätige.) Es ist hoffentlich offensichtlich, dass die Methodendefinition wissen muss, dass sie einen Parameter als Referenz empfängt, da sie das Änderungsverhalten ändert (z. B. mit =
). Sie könnten jedoch argumentieren, dass es der Laufzeit egal ist (in solchen statisch typisierten Sprachen, in denen der Laufzeitversand nominell informiert ist): Sie muss nur diesen Wert vom Anrufer an den Angerufenen senden Es ist egal, ob es sich um eine Referenz handelt oder nicht, dies ist ein Problem für die Anrufstelle und das Anrufziel.
Lockerer könnte sich auf „Anrufstelle“ beziehen der Code, der einen Anruf anzeigt, sowie der „zugehörige“ Code um ihn herum (im Gegensatz nur zum „Anruf“). In C # könnte man sich beispielsweise auf das Definieren einer Dummy-Variablen „an“ einer Aufrufstelle beziehen, z. beim Aufrufen einer TryParse
Methode:
int dummy; bool looksLikeAnInt = int.TryParse(str, out dummy);
Dieser ganze „Block“ könnte als Teil der Anrufstelle betrachtet werden. Ich würde dies als weniger „präzise“ Bedeutung betrachten, aber das hindert mich nicht daran, es zu verwenden.