Leaderboard - JIT Compiled (Weniger ist besser)
- es1024 - 81.2 Punkte (einschließlich eines funktionierenden Compilers!)
- Kieth Randall - 116 Punkte
- Ell - 121 Punkte
Bestenliste - Ausgelegt (Weniger ist besser)
- Martin Büttner - 706654 Punkte (ungefähr 2 Stunden).
- Kriptychon - 30379 Punkte (97 Sekunden)
Wenn Sie dies akzeptieren, besteht Ihre Aufgabe darin, den kleinstmöglichen Bytecode-Interpreter / VM zu schreiben. Der VM / Interpreter verwendet eine kleine CISC-Architektur (Operationen können unterschiedlich groß sein) mit der unten angegebenen Sprache. Nach Abschluss müssen Sie den Wert der 3 CPU-Register drucken, um zu beweisen, dass die richtige Ausgabe gedruckt wird (3.126.900.366).
Compiler
Wenn Sie Ihre eigenen Tests durchführen möchten, finden Sie unten einen Compiler. Fühlen Sie sich frei, Ihre Tests mit Ihrer Antwort zu posten.
"VM" Spezifikationen
Die VM hat 3 vorzeichenlose 32-Bit-Integralregister: R0, R1, R2. Sie werden hexadezimal als 0x00, 0x01 und 0x02 dargestellt.
Die folgenden Vorgänge müssen unterstützt werden:
Das Format ist [Name] [... Operanden ...], [hexadezimaler Op-Code] [... Operanden wiederholt ...]
- LOAD [Register] [4-Byte-Wert], 0x00 [Register] [4-Byte-Wert]
- PUSH [register], 0x02 [register]
- POP [register], 0x03 [register]
- ADD [Register, 1 Byte] [Register, 1 Byte], 0x04 [Register] [Register]
- SUB [Register, 1 Byte] [Register, 1 Byte], 0x05 [Register] [Register]
- MUL [Register, 1 Byte] [Register, 1 Byte], 0x06 [Register] [Register]
- DIV [Register, 1 Byte] [Register, 1 Byte], 0x07 [Register] [Register]
- JMP [Codezeile, 4 Byte], 0x08 [4 Byte Codezeilennummer]
- CMP [Register, 1 Byte] [Register, 1 Byte], 0x09 [Register] [Register]
- BRANCHLT [Codezeile, 4 Byte], 0x0a [4 Byte Codezeilennummer]
Einige Notizen:
- Die obigen mathematischen Operationen addieren die Werte von 2 Registern und platzieren die Ausgabe im ersten Register.
- CMP, der Vergleichsoperator, sollte die Werte von 2 Registern vergleichen und die Ausgabe in einem internen Flag (dies kann implementierungsspezifisch sein) für die zukünftige Verwendung in Verzweigungsanweisungen speichern.
- Wenn BRANCH vor CMP aufgerufen wird, sollte die "VM" nicht verzweigen, es sei denn, BRANCHEQ wird aufgerufen.
- PUSH / POP drücken oder knacken Zahlen aus dem Stapel.
- Sprung- und Verzweigungsoperatoren springen zu einer bestimmten Operation (Codezeile), nicht zu einer binären Adresse.
- Verzweigungsoperationen führen den Vergleich nicht durch. Sie nehmen vielmehr die Ausgabe des letzten Vergleichs zur Ausführung.
- Branch- und Jump-Operatoren verwenden ein auf Null basierendes Indizierungssystem für Zeilennummern. (ZB JMP 0 springt in die erste Zeile)
- Alle Operationen müssen an vorzeichenlosen Zahlen ausgeführt werden, die auf Null überlaufen und bei einem Ganzzahlüberlauf keine Ausnahme auslösen.
- Eine Division durch Null ist nicht zulässig und daher ist das Verhalten des Programms nicht definiert. Sie können (zum Beispiel) ...
- Programm zum Absturz bringen.
- Beenden Sie die Ausführung der VM und geben Sie den aktuellen Status zurück.
- Meldung "ERR: Division by 0" anzeigen.
- Die Beendigung des Programms ist definiert als wenn der Befehlszeiger das Programmende erreicht (ein nicht leeres Programm kann angenommen werden).
Ausgabe Die Ausgabe muss genau das sein (Zeilenumbrüche inklusive)
R0 3126900366
R1 0
R2 10000
Punkte
Punkte werden nach folgender Formel berechnet:Number Of Characters * (Seconds Needed To Run / 2)
Um zu vermeiden, dass Hardwareunterschiede zu unterschiedlichen Zeiten führen, wird jeder Test auf meinem Computer (i5-4210u, 8 GB RAM) auf einem Ubuntu-Server oder unter Windows 8 ausgeführt. Versuchen Sie daher, keine wahnsinnig exotische Laufzeit zu verwenden, die nur auf einem Dual G5 kompiliert wird Mac Pro mit genau 762,66 MB freiem RAM.
Wenn Sie eine spezielle Laufzeit / Sprache verwenden, senden Sie bitte einen Link zu dieser.
- Für Interessenten habe ich den Testcode (geschrieben in C #) hier veröffentlicht: http://pastebin.com/WYCG5Uqu
Testprogramm
Die Idee kam von hier , also werden wir eine etwas modifizierte Version ihres Programms verwenden.
Die korrekte Ausgabe für das Programm lautet: 3.126.900.366
In C:
int s, i, j;
for (s = 0, i = 0; i < 10000; i++) {
for (j = 0; j < 10000; j++)
s += (i * j) / 3;
}
Im Code: [R0 steht stellvertretend für s, R1 für j, R2 für i]
LOAD R0 0
LOAD R2 0 <--outer loop value
LOAD R1 0 <--inner loop value
--Begin inner loop--
PUSH R1 <--push inner loop value to the stack
MUL R1 R2 <--(i*j)
PUSH R2
LOAD R2 3
DIV R1 R2 <-- / 3
POP R2
ADD R0 R1 <-- s+=
POP R1
PUSH R2
LOAD R2 1
ADD R1 R2 <--j++
POP R2
PUSH R2
LOAD R2 10000
CMP R1 R2 <-- j < 10000
POP R2
BRANCHLT 3 <--Go back to beginning inner loop
--Drop To outer loop--
LOAD R1 1
ADD R2 R1 <--i++
LOAD R1 10000
CMP R2 R1 <-- i < 10000
LOAD R1 0 <--Reset inner loop
BRANCHLT 2
In binär / hex:
0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x02 0x00 0x00 0x00 0x00
0x00 0x01 0x00 0x00 0x00 0x00
0x02 0x01
0x06 0x01 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x03
0x07 0x01 0x02
0x03 0x02
0x04 0x00 0x01
0x03 0x01
0x02 0x02
0x00 0x02 0x00 0x00 0x00 0x01
0x04 0x01 0x02
0x03 0x02
0x02 0x02
0x00 0x02 0x00 0x00 0x27 0x10
0x09 0x01 0x02
0x03 0x02
0x0a 0x00 0x00 0x00 0x03
0x00 0x01 0x00 0x00 0x00 0x01
0x04 0x02 0x01
0x00 0x01 0x00 0x00 0x27 0x10
0x09 0x02 0x01
0x00 0x01 0x00 0x00 0x00 0x00
0x0a 0x00 0x00 0x00 0x02
Bonuspunkte (Effekte werden multipliziert angewendet) Wenn Sie sich beispielsweise für alle drei qualifizieren, sind dies ((Zeichen * 0,50) * 0,75) * 0,90
- Reduzierung um 50%, wenn der Interpreter tatsächlich ein JIT-Compiler ist
- Reduzierung um 25%, wenn eine Art Loop-Unrolling / sinnvolle Optimierung angewendet wird.
- 10% weniger, wenn Sie die VM mit erweitern
- BRANCHEQ [Codezeile, 4 Bytes] (Verzweigen, wenn gleich - Opcode 0x0b)
- BRANCHGT [Codezeile, 4 Bytes] (Branch, wenn größer als - opcode 0x0c)
- BRANCHNE [Codezeile, 4 Bytes] (Verzweigen, wenn nicht gleich - Opcode 0x0d)
- RLOAD [register 1] [register 2] (verschiebe den Wert von register 2 nach register 1 - opcode 0x01).
Nicht erlaubt
- Das Vorkompilieren des Testfalls in das Programm ist verboten. Sie müssen entweder den Bytecode von STDIN oder von einer Datei akzeptieren (spielt keine Rolle, welche).
- Die Ausgabe zurückgeben, ohne das Programm auszuführen.
- Eine andere Möglichkeit, die VM-Anforderung zu umgehen.
quelle
CMP
auf weniger als oder Gleichheit? Und was passiert mit dem Ergebnis?MUL
undDIV
sind auch unterbestimmt. Sollten sie unterschrieben oder nicht unterschrieben sein? Was passiert beim Multiplikationsüberlauf?Antworten:
C 752 (589 + 163 zum Definieren von Flags) * 0,5 (JIT) * 0,9 (Erweiterungen) * (0,75 Optimierung) * (0,64 Sekunden / 2) = 81,216
Nimmt Code (
LOAD R0
usw.), keine abschließenden Zeichen, ein Leerzeichen, keine Leerzeilen in der Mitte, keine Kommentare usw. abschließende Zeilenumbrüche erforderlich.Dieser wird dann in 80386 Bytecode konvertiert und ausgeführt.
Das Laden
0
in ein Register wird ersetzt, indemxor
das Register mit sich selbst anstatt in das Registermov
geladen wird0
, das im generierten Bytecode drei Bytes kürzer ist und möglicherweise sehr geringfügig schneller ist.Kompilieren mit:
POSIX-kompatibles Betriebssystem erforderlich.
Die Eingabe wird aus STDIN gelesen (wird
./bytecode < file
zum Pipeen aus einer Datei verwendet).Resultierender Bytecode für das Testprogramm:
Ungolfed:
quelle
C, Score = 854 Byte × (~ 0,8 s / 2) × 0,5 [JIT] × 0,9 [Extensions] = ~ 154 Byte Sek
Kompilieren Sie mit
gcc vm.c -ovm -m32 -w
einem x86 POSIX-kompatiblen Betriebssystem.Führen Sie mit
./vm < program
, woprogram
eine binäre Programmdatei ist.An der Geschwindigkeit arbeiten. Das Programm führt eine recht einfache Übersetzung des Eingabeprogramms in x86-Maschinencode durch und überlässt den Rest der CPU.
Hier ist zum Beispiel die Übersetzung des Testprogramms.
ecx
,esi
Undedi
entsprichtR0
,R1
undR2
verbunden ist;bh
hält die Statusflags;eax
undedx
sind Rubbelregister; Der Aufruf-Stack entspricht dem Stack der VM:Ungolfed
Code-Snippet anzeigen
quelle
CJam,
222187185 Bytes * (zu langsam / 2)Ich wollte nur sehen, wie kurz ich eine Bytecode-VM bekommen kann, indem ich sie in CJam schreibe. Weniger als 200 Bytes scheinen ziemlich anständig zu sein. Es ist verdammt langsam, weil CJam selbst interpretiert wird. Das Ausführen des Testprogramms dauert Ewigkeiten.
Um es auszuführen, laden Sie den Java-Interpreter unter diesem SourceForge-Link herunter , speichern Sie den Code in
vm.cjam
und führen Sie ihn mit ausDas Programm erwartet den Bytecode auf STDIN. Ich habe keine Möglichkeit , noch Rohr binäre Daten in ein Programm gefunden, ohne Powershell ein abschließendes Zeilenumbruch Hinzufügen und Konvertieren
0x0a
zu0x0d 0x0a
, was wirklich ärgerlich ist. Der Code enthält 4 Bytes zum Korrigieren von that (D-);
), die ich nicht in die Gesamtzahl einbezogen habe, da das Programm diesen Bytecode nicht auf STDIN empfangen sollte, sondern nur in einer seltsam codierten Version . Wenn jemand eine Lösung dafür kennt, lass es mich wissen.Leicht ungolfed:
Ich werde morgen eine richtige Erklärung hinzufügen.
Kurz gesagt, ich speichere alle Register, den Befehlszeiger und das Vergleichsflag in Variablen, damit ich den Stapel von CJam freihalten kann, um ihn als Stapel der VM zu verwenden.
quelle
python / c ++, score = 56.66
1435 Zeichen * .234 / 2 Sekunden * .5 [JIT] * .75 [Optimierung] * .90 [Zusätzliche Anweisungen]
Kompiliert das Eingabeprogramm nach c ++, führt gcc darauf aus und führt dann das Ergebnis aus. Die meiste Zeit wird in gcc verbracht.
Die eine Optimierung, die ich mache, ist, die Stapeloperationen auf explizite Variablen zu reduzieren, wenn es semantisch erlaubt ist. Es hilft viel, die Laufzeit des kompilierten Codes um das 10-fache zu verbessern (etwa 0,056 Sekunden, um die resultierende Binärdatei tatsächlich auszuführen). Ich bin nicht sicher, was gcc tut, um diese Verbesserung zu erzielen, aber es ist gut.
Könnte sicherlich noch etwas mehr golfen werden.
quelle
Lua 5.2 (oder LuaJIT), 740 Bytes
Erster Versuch, nur minimal golfen. Diese Version funktioniert (zumindest im Testprogramm) und implementiert die zusätzlichen Opcodes, erfüllt jedoch nicht die vorzeichenlosen mathematischen Anforderungen und ist nicht besonders schnell. Als Bonus ist es jedoch eine VM, die in einer VM ausgeführt wird und so geschrieben ist, dass sie entweder interpretiert (ausgeführt mit PUC-Lua) oder als JIT (ausgeführt mit LuaJIT; weiterhin interpretiert, aber der Interpreter ist es jetzt JITted).
EDIT: Golf besser, immer noch groß.
BEARBEITEN: Ein schwerwiegender Fehler wurde behoben und die Arithmetik ist jetzt auf den
unsigned long
Bereich beschränkt. Irgendwie ist es gelungen, die Größe davon abzuhalten, außer Kontrolle zu geraten, aber es gibt immer noch die falsche Antwort.EDIT: Es stellte sich heraus, dass das Ergebnis korrekt war, die Ausgabe jedoch nicht. Umgestellt auf Drucken mit
%u
statt%d
und alles ist gut. Es wurden auch tabellenbasierte Register für Variablen herausgeschaltet, um die Größe und Geschwindigkeit etwas zu verbessern .BEARBEITEN: Unter Verwendung der
goto
Anweisung von Lua 5.2 (auch in LuaJIT verfügbar) habe ich den Interpreter durch "JIT-to-Lua" ersetzt, wodurch Code generiert wird, der direkt von der Lua-VM selbst ausgeführt wird. Ich bin nicht sicher, ob dies wirklich als JIT gilt, aber es verbessert die Geschwindigkeit.Hier ist die ursprüngliche, lesbare Version.
quelle
<
in meinen Schleifen stattdessen verwendet<=
, sodass die letzte Verzweigungsanweisung weggelassen wurde. Es wird immer noch die falsche Antwort angezeigt, aber jetzt dauert es ein paar Minuten, bis es fertig ist. :)C #
15051475 BytesDies ist meine Version des Interpreters, die in C # geschrieben wurde. Ich glaube, ich könnte mehr optimiert / golfen, aber ich weiß nicht wirklich, wo;)
Golf Version:
bearbeiten
einige unnötige
public
undprivate
Modifikatoren entfernt:nennen Sie es mit
executable.exe filename
wofilename
ist die Datei, die den zu interpretierenden Code enthältMein "Testprogramm":
Dolmetscher ungolfed mit längeren Namen Variablen, Klassen, ...
quelle