Frage

OK, zuerst möchte ich hier keine Flammwirtschaft oder ähnliches. Meine größere Frage ist theoretischer und wird nur wenige Beispiele enthalten.

Wie ich schrieb, kann ich also nicht verstehen, wie die interpretierte Sprache noch wenig effizient sein kann. Und da es modern ist, werde ich Java als Beispiel nehmen.

Kehren wir zu Tagen zurück, an denen es keine JIT -Compiler gab. Java hat seine virtuelle Maschine, die im Grunde seine Hardware ist. Sie schreiben Code, als er in Bytecode zusammengestellt wurde, um mindestens einen Job von der virtuellen Maschine zu nehmen, das ist in Ordnung. In Anbetracht der Tatsache, wie komplex sogar RISC -Anweisungssatz in Hardware sein kann, kann ich mir nicht einmal überlegen, wie ich es bei Software -emuliertem Hardware tun kann.

Ich habe keine Erfahrung beim Schreiben von virtuellen Maschinen, daher weiß ich nicht, wie es höchstens effizienter ist, aber ich kann mir nichts eFifcient vorstellen, als jeden Anweisungen für Match ADN zu testen, als angemessene Aktionen zu tun. Sie wissen, so etwas wie: if(instruction=="something") { (do it) } else if(instruction=="something_diffrent"){ (do it) }etc....

Aber das muss furchtbar langsam sein. Und trotzdem gibt es auch Artikel, die Java vor JIT -Compilern langsam war, und sagen immer noch, dass es nicht so langsam ist. Um es zu emulieren, muss es jedoch viele Taktzyklen von echtem HW benötigen, um einen Bytecode -Befehl durchzuführen.

Und dennoch basieren sogar ganze Plattformen auf Java. Zum Beispiel Android. Und erste Verisons von Android hatte keinen JIT -Compiler. Sie wurden interpretiert. Aber sollte Android nicht furchtbar langsam sein? Und doch ist es nicht. Ich weiß, wenn Sie eine API -Funktion aufrufen, sind sie aus der Android -Bibliothek in Maschinencode geschrieben, sodass sie effizient sind, sodass dies sehr hilft.

Aber stellen Sie sich vor, Sie würden Ihre eigene Spieleima aus SRATCH schreiben, indem Sie API nur zum Anzeigen von Bildern verwenden. Sie müssten viele Array -Kopiervorgänge durchführen, viele Berechnungen, die beim Emulieren furchtbar langsam wären.

Und jetzt einige Beispiele, wie ich versprochen habe. Da ich hauptsächlich mit MCUS arbeite, habe ich JVM für Atmel Avr MCU gefunden. Der Staat, dass 8MHz MCU 20k Java Optcodes pro Sekunde durchführen kann. Da AVR jedoch die meisten Anweisungen in ein oder zwei Zyklen ausführen kann, sagen wir 6000000 Anweisungen durchschnittlich. Dies gibt uns, dass JVM ohne JIT -Compiler 300 -mal langsamer für Maschinencode ist. Warum also Java ohne JIT -Compiler so beliebt werden? Ist das nicht zu schlechtem Leistungsverlust? Ich kann es einfach nicht verstehen. Vielen Dank.

War es hilfreich?

Lösung

Wir haben schon lange Byte -Code. Auf dem alten Apple II war das USCD P-System sehr beliebt, das Pascal in Byte-Code zusammenstellte, das von einem 8-Bit 6502 interpretiert würde, der möglicherweise bei 2 MHz läuft. Diese Programme sind einigermaßen schnell gelaufen.

Ein Bytecode -Interpreter würde im Allgemeinen eher auf einer Sprungtabelle als auf einer Kette von basieren if/then/else Aussagen. In C oder C ++ würde dies a beinhalten switch Aussage. Grundsätzlich hätte der Interpreter das Äquivalent eines Arrays von Verarbeitungscode und verwenden den Opcode in der Byte -Code -Anweisung als Index des Arrays.

Es ist auch möglich, Byte-Code zu haben, der höher als die Maschinenanweisungen ist, so dass eine Byte-Code-Anweisung in mehrere, manchmal zahlreiche Anweisungen für Maschinencode übersetzt wurde. Ein Byte -Code, der für eine bestimmte Sprache konstruiert wurde, kann dies ziemlich einfach tun, da er nur mit den Kontroll- und Datenstrukturen dieser bestimmten Sprache übereinstimmen muss. Dies streckt den Interpretationsaufwand aus und macht den Dolmetscher effizienter.

Eine interpretierte Sprache hat wahrscheinlich eine gewisse Geschwindigkeitsstrafe im Vergleich zu einer kompilierten Sprache, dies ist jedoch oft unwichtig. Viele Programme verarbeiten Eingabe und Ausgabe mit menschlicher Geschwindigkeit, und das lässt eine enorme Leistung, die verschwendet werden kann. Sogar ein netzwerkgebundenes Programm dürfte weitaus mehr CPU-Strom zur Verfügung haben als es benötigt. Es gibt Programme, die alle CPU -Effizienz nutzen können, die sie erhalten können, und aus offensichtlichen Gründen tendenziell nicht in interpretierten Sprachen geschrieben.

Und natürlich stellt sich die Frage, was Sie für eine Ineffizienz bekommen, die möglicherweise einen Unterschied machen kann oder nicht. Interpretierte Sprachimplementierungen sind in der Regel einfacher zu portieren als kompilierte Implementierungen, und der tatsächliche Byte -Code ist häufig tragbar. Es kann einfacher sein, eine höhere Funktionalität in die Sprache aufzunehmen. Es ermöglicht den Kompilierungsschritt viel kürzer, was bedeutet, dass die Ausführung viel schneller beginnen kann. Es kann eine bessere Diagnostik ermöglichen, wenn etwas schief geht.

Andere Tipps

Aber sollte Android dann nicht furchtbar langsam sein?

Definieren "schrecklich langsam". Es ist ein Telefon. Es muss "erste Zifferblatt" verarbeiten, bevor Sie die zweite Ziffer wählen.

In jeder interaktiven Anwendung ist der begrenzende Faktor immer menschliche Reaktionszeit. Es könnte eine 100 -köpfige Zeit langsamer sein und immer noch schneller sein als der Benutzer.

Um Ihre Frage zu beantworten, sind Dolmetscher langsam, aber sie sind normalerweise schnell genug, zumal die Hardware immer schneller wird.

Denken Sie daran, als Java vorgestellt wurde, wurde es als Web-Applet-Sprache verkauft (ersetzt und jetzt durch JavaScript ersetzt-, die ebenfalls interpretiert wurde). Erst nach der Zusammenstellung von JIT wurde es auf Servern populär.

Bytecode -Dolmetscher können schneller sein als eine Linie von if () s mit einer Sprungtabelle:

 void (*jmp_tbl)[256] = ...;  /* array of function pointers */
 byte op = *program_counter++;
 jmp_tbl[op]();

Es gibt zwei verschiedene Möglichkeiten, sich dieser Frage zu nähern.

(i) "Warum ist es in Ordnung, langsamen Code auszuführen"

Wie James bereits oben erwähnt, ist manchmal die Ausführungsgeschwindigkeit nicht alles, woran Sie interessiert sind. Für viele Apps, die im interpretierten Modus ausgeführt werden, können "schnell genug" sein. Sie müssen berücksichtigen, wie der Code, den Sie schreiben, verwendet werden.

(ii) "Warum wird der Code für den Code für Bewusstsein interpretiert?"

Es gibt viele Möglichkeiten, wie Sie einen Dolmetscher implementieren können. In Ihrer Frage sprechen Sie über den naivsten Ansatz: im Grunde genommen ein großer Schalter und interpretieren Sie jede JVM -Anweisung, wie er gelesen wird.

Sie können dies jedoch optimieren: Anstatt sich beispielsweise eine einzelne JVM -Anweisung anzusehen, können Sie sich eine Sequenz von ihnen ansehen und nach Mustern suchen, für die Sie effizientere Interpretationen zur Verfügung haben. Suns JVM führt tatsächlich einige dieser Optimierungen im Dolmetscher selbst durch. In einem früheren Job nahm ein Mann einige Zeit, um den Dolmetscher auf diese Weise zu optimieren, und interpretierte Java -Bytecode nach seinen Veränderungen merklich schneller.

Aber in modernen JVMs, die einen JIT -Compiler enthalten, ist der Dolmetscher nur ein Sprungbrett, bis die JIT seine Arbeit erledigt. Die Leute verbringen also nicht wirklich so viel Zeit damit, den Dolmetscher zu optimieren.

12 MHz wäre ein ATTINY, ein 8-Bit-Mikroprozessor. Das bedeutet (z. B.), dass ein nationaler Anweisungen nur zwei 8-Bit-Zahlen zusammen hinzufügen kann, um ein 9-Bit-Ergebnis zu erzielen. Das JVM ist im Grunde ein virtueller 32-Bit Bitzahlen zusammen, um ein 33-Bit-Ergebnis zu erzielen.

Wenn Sie die Anweisungsraten vergleichen, sollten Sie daher eine Verringerung der Anweisungsrate um 4: 1 als absolutes Minimum erwarten. In Wirklichkeit, obwohl es einfach ist, einen 32-Bit-Add mit 4 8-Bit-Adds (mit Tragen) zu simulieren, skalieren einige Dinge nicht ganz so. Nur zum Beispiel nach Atmel's Own APP -Hinweis, Eine 16x16-Multiplikation, die ein 32-Bit-Ergebnis erzeugt, führt in ~ 218 Taktzyklen aus. Der gleiche App-Hinweis zeigt eine 16/16-Bit-Division (erzeugt ein 8-Bit-Ergebnis), das in 255 Zyklen ausgeführt wird.

Unter der Annahme, dass diese linear skalieren, können wir erwarten, dass 32-Bit-Versionen der Multiplikation ~ 425-450 Taktzyklen und die Abteilung ~ 510 Zyklen dauern. In Wirklichkeit sollten wir wahrscheinlich ein bisschen Overhead erwarten, was die Geschwindigkeit immer noch mehr verringern würde. Dies macht sie wahrscheinlich realistischer.

Fazit: Wenn Sie Äpfel mit Äpfeln vergleichen, wird deutlich, dass ein Großteil des Geschwindigkeitsunterschieds, über den Sie sprechen, überhaupt nicht real ist (oder dass JVM Overhead sowieso nicht zuzuordnen ist).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top