Warum gibt es lokale Variablen im Stack-basierten IL-Bytecode?

8

Warum gibt es in einer stackbasierten Zwischensprache wie CIL oder Java-Bytecode lokale Variablen? Man könnte einfach nur den Stack verwenden. Vielleicht nicht so einfach für handgefertigte IL, aber ein Compiler kann es sicherlich tun. Aber mein C # -Compiler nicht.

Sowohl der Stapel als auch die lokalen Variablen sind für die Methode privat und gehen bei der Rückkehr der Methode nicht in den Gültigkeitsbereich. Es kann also nichts mit Nebenwirkungen zu tun haben, die von außerhalb der Methode sichtbar sind (von einem anderen Thread).

Ein JIT-Compiler würde bei der Generierung von Maschinencode Lasten und Speicher auf beide Stack-Slots und lokale Variablen eliminieren, wenn ich richtig liege, daher sieht der JIT-Compiler auch keine Notwendigkeit für lokale Variablen.

Andererseits generiert der C # -Compiler Lasten und speichert lokale Variablen, selbst wenn das Kompilieren mit aktivierten Optimierungen erfolgt. Warum?

Nehmen Sie zum Beispiel den folgenden erfundenen Beispielcode:

%Vor%

Bei der Kompilierung in C # wird mit Optimierungen Folgendes erzeugt:

%Vor%

Beachten Sie die Lasten und speichert sie in den vier lokalen Variablen. Ich könnte die gleichen Operationen schreiben (abgesehen von der offensichtlichen Optimierung der Konstantenausbreitung), ohne lokale Variablen zu verwenden.

%Vor%

Es scheint mir richtig und viel kürzer und effizienter. Warum haben stapelbasierte Zwischensprachen also lokale Variablen? Und warum benutzt der optimierende Compiler sie so umfassend?

    
Virtlink 16.09.2012, 23:38
quelle

2 Antworten

4

Je nach Situation, insbesondere wenn es sich um Aufrufe handelt, bei denen die Parameter neu angeordnet werden müssen, um dem Aufruf zu entsprechen, reicht ein reiner Stack nicht aus, wenn Ihnen keine Register oder Variablen zur Verfügung stehen. Wenn Sie nur diesen Stack erstellen möchten, benötigen Sie zusätzliche Funktionen zur Stapelmanipulation, z. B. die Möglichkeit, die beiden obersten Elemente des Stapels auszutauschen / auszutauschen.

Obwohl es in diesem Fall möglich ist, alles als reinen Stack-basierten Ausdruck auszudrücken, kann es dem Code eine Menge Komplexität hinzufügen, ihn aufblähen und seine Optimierung erschweren (lokale Variablen sind ideal) Kandidaten für die Zwischenspeicherung in Registern).

Denken Sie auch daran, dass Sie in .NET Parameter per Referenz übergeben können. Wie könnten Sie die IL für diesen Methodenaufruf ohne eine lokale Variable erstellen?

%Vor%     
Lucero 16.09.2012, 23:48
quelle
0

Diese Antwort ist rein spekulativ - aber ich vermute, dass die Antwort aus drei Teilen besteht.

1: Der Code, der dazu verwendet wird, Dup gegenüber lokalen Variablen vorzuziehen, ist sehr nicht trivial, selbst wenn Sie Nebenwirkungen ignorieren. Es fügt der Optimierung viel Komplexität und möglicherweise viel Ausführungszeit hinzu.

2: Sie können die Nebenwirkungen nicht ignorieren. In dem Beispiel, in dem alles nur ein Literal ist, ist es sehr einfach zu wissen, dass die Werte in Stack oder Locals sind und daher die vollständige Kontrolle über die aktuellen Anweisungen haben. Sobald diese Werte aus den Heap-, statischen Speicher- oder Methodenaufrufen stammen, können Sie nicht mehr Dinge herummischen, um Dup anstelle von Locals zu verwenden. Das Ändern der Reihenfolge kann die Funktionsweise der Dinge ändern und unbeabsichtigte Konsequenzen aufgrund von Nebenwirkungen oder externem Zugriff auf den gemeinsamen Speicher verursachen. Dies bedeutet, dass Sie diese Optimierungen normalerweise nicht vornehmen können.

3: Die Annahme, dass Stapelwerte schneller als lokale Variablen sind, ist keine gute Annahme - es kann für eine bestimmte IL- & gt; Maschinencodetransformation gelten, dass Stapelwerte schneller sind, aber es gibt keinen Grund, warum ein Smart JIT würde keinen Stapelspeicherplatz und eine lokale Variable in ein Register setzen. Es ist die Aufgabe des JIT zu wissen, was schnell und was langsam für die aktuelle Maschine ist, und es ist die Aufgabe des JIT, das Problem zu lösen. Der CIL-Compiler hat per Entwurf keine Antwort darauf, ob Locals oder Stack schneller sind. und so ist der messbare Unterschied zwischen diesen Ergebnissen nur in der Codegröße.

Zusammengenommen bedeutet 1, dass es schwer ist und nicht-triviale Kosten hat, 2 bedeutet, dass Fälle in der realen Welt, in denen es wertvoll wäre, wenige sind, und 3 bedeutet, dass 1 und 2 sowieso irrelevant sind.

Selbst wenn das Ziel darin besteht, die CIL-Größe zu minimieren, was ein messbares Ziel für den CIL-Compiler ist, beschreibt Grund # 2 dies als eine kleine Verbesserung für eine kleine Anzahl von Fällen. Das Paretoprinzip kann uns nicht sagen, dass es eine SCHLECHTE Idee ist, diese Art der Optimierung zu implementieren, aber es wird empfehlen, dass es wahrscheinlich eine bessere Nutzung der Entwicklerzeit gibt.

    
danwyand 17.12.2013 22:14
quelle

Tags und Links