Leaderboard - JIT Compiled (Lower is better)
- es1024 - 81,2 points (y compris un compilateur fonctionnel!)
- Kieth Randall - 116 points
- Ell - 121 points
Classement - Interprété (plus c'est bas, mieux c'est)
- Martin Büttner - 706654 points (environ 2 heures).
- manuscrit - 30379 points (97 secondes)
Votre mission, si vous l'acceptez, est d'écrire le plus petit interprète / VM de bytecode possible. La VM / interprète utilise une petite architecture CISC (les opérations peuvent varier en taille), avec la langue spécifiée ci-dessous. Une fois terminé, vous devez imprimer la valeur des 3 registres CPU pour prouver que la sortie correcte est imprimée (3 126 900 366).
Compilateur
Si vous souhaitez faire vos propres tests, un compilateur est affiché ci-dessous. N'hésitez pas à poster vos tests avec votre réponse.
Spécifications "VM"
La VM a 3 registres intégraux non signés 32 bits: R0, R1, R2. Ils sont représentés en hexadécimal comme 0x00, 0x01 et 0x02.
Les opérations suivantes doivent être prises en charge:
Le format est [nom] [... opérandes ...], [code op hexadécimal] [... opérandes répétés ...]
- LOAD [registre] [valeur 4 octets], 0x00 [registre] [valeur 4 octets]
- PUSH [registre], 0x02 [registre]
- POP [registre], 0x03 [registre]
- ADD [registre, 1 octet] [registre, 1 octet], 0x04 [registre] [registre]
- SUB [registre, 1 octet] [registre, 1 octet], 0x05 [registre] [registre]
- MUL [registre, 1 octet] [registre, 1 octet], 0x06 [registre] [registre]
- DIV [registre, 1 octet] [registre, 1 octet], 0x07 [registre] [registre]
- JMP [ligne de code, 4 octets], 0x08 [numéro de ligne de code de 4 octets]
- CMP [registre, 1 octet] [registre, 1 octet], 0x09 [registre] [registre]
- BRANCHLT [ligne de code, 4 octets], 0x0a [numéro de ligne de code 4 octets]
Quelques notes:
- Les opérations mathématiques ci-dessus additionnent les valeurs de 2 registres ensemble, plaçant la sortie dans le premier registre.
- CMP, l'opérateur de comparaison, doit comparer les valeurs de 2 registres et stocker la sortie dans un indicateur interne (cela peut être spécifique à l'implémentation) pour une utilisation future sur les instructions de branchement.
- Si BRANCH est appelé avant CMP, sauf si BRANCHEQ est appelé, la «VM» ne doit pas se ramifier.
- PUSH / POP sans surprise, push ou pop numéros de la pile.
- Les opérateurs de saut et de branche passent à une opération spécifique (ligne de code), et non à une adresse binaire.
- Les opérations des succursales ne font pas la comparaison. Au lieu de cela, ils prennent la sortie de la dernière comparaison pour s'exécuter.
- Les opérateurs de branche et de saut utilisent un système d'indexation de numéro de ligne basé sur zéro. (Par exemple, JMP 0 saute à la première ligne)
- Toutes les opérations doivent être effectuées sur des nombres non signés qui débordent à zéro et ne lèvent pas d'exception sur un dépassement d'entier.
- La division par zéro n'est pas autorisée et en tant que tel, le comportement du programme n'est pas défini. Vous pouvez (par exemple) ...
- Arrêtez le programme.
- Arrêtez l'exécution de la machine virtuelle et retournez son état actuel.
- Afficher un message "ERR: Division by 0".
- La fin du programme est définie comme lorsque le pointeur d'instruction atteint la fin du programme (un programme non vide peut être supposé).
Sortie La sortie doit être exactement celle-ci (nouvelles lignes incluses)
R0 3126900366
R1 0
R2 10000
Points Les
points sont calculés selon la formule suivante:Number Of Characters * (Seconds Needed To Run / 2)
Pour éviter les différences matérielles provoquant des heures différentes, chaque test sera exécuté sur mon ordinateur (i5-4210u, 8 Go de RAM) dans le serveur Ubuntu ou Windows 8, alors essayez de ne pas utiliser un runtime fou-exotique qui ne se compile que sur un Dual G5 Mac Pro avec exactement 762,66 Mo de RAM libre.
Si vous utilisez un runtime / langage spécialisé, veuillez poster un lien vers celui-ci.
- Pour les parties intéressées, j'ai publié le code de test (écrit en C #) ici: http://pastebin.com/WYCG5Uqu
Programme de test
L'idée est venue d' ici , nous allons donc utiliser une version quelque peu modifiée de leur programme.
La sortie correcte pour le programme est: 3 126 900 366
En C:
int s, i, j;
for (s = 0, i = 0; i < 10000; i++) {
for (j = 0; j < 10000; j++)
s += (i * j) / 3;
}
Dans le code: [R0 est représentatif de s, R1 de j, R2 de 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
En binaire / 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
Points bonus (les effets sont appliqués de manière multiplicative) Par exemple, si vous vous qualifiez pour les trois, ce serait ((caractères * 0,50) * 0,75) * 0,90
- Baisse de 50% si l'interpréteur est en fait un compilateur JIT
- 25% de diminution si elle applique une quelconque sorte de déroulement de boucle / optimisation significative.
- 10% de diminution si vous étendez la machine virtuelle avec
- BRANCHEQ [ligne de code, 4 octets] (Branche si égale - opcode 0x0b)
- BRANCHGT [ligne de code, 4 octets] (Branche si supérieure à - opcode 0x0c)
- BRANCHNE [ligne de code, 4 octets] (Branche si différente - opcode 0x0d)
- RLOAD [registre 1] [registre 2] (déplacez la valeur du registre 2 vers le registre 1 - opcode 0x01).
Rejeté
- La précompilation du cas de test dans le programme est interdite. Vous devez accepter le bytecode de STDIN ou d'un fichier (peu importe lequel).
- Renvoyer la sortie sans exécuter le programme.
- De toute autre manière à laquelle vous pouvez penser pour tromper l'exigence VM.
la source
CMP
Vérifie- t-il moins que ou l'égalité? Et qu'advient-il de son résultat?MUL
etDIV
sont également sous-spécifiés. Doivent-ils être signés ou non signés? Que se passe-t-il en cas de débordement de multiplication?Réponses:
C, 752 (589 + 163 pour définir les drapeaux) * 0,5 (JIT) * 0,9 (extensions) * (0,75 optimisation) * (0,64 seconde / 2) = 81,216
Prend du code (
LOAD R0
, etc.), pas de caractères de fin, un seul espace, pas de lignes vides au milieu, pas de commentaires, etc. Retour à la ligne requis.Il est ensuite converti en 80386 bytecode et exécuté.
Le chargement
0
dans un registre est remplacé parxor
ing le registre avec lui-même au lieu d'mov
ing0
dans le registre, qui est trois octets plus court dans le bytecode généré, et peut être très légèrement plus rapide.Compiler avec:
Système d'exploitation compatible POSIX requis.
L'entrée est lue depuis STDIN (à utiliser
./bytecode < file
pour diriger à partir d'un fichier).Bytecode résultant pour le programme de test:
Non golfé:
la source
C, Score = 854 octets × (~ 0,8 s / 2) × 0,5 [JIT] × 0,9 [Extensions] = ~ 154 octets s
Compilez avec
gcc vm.c -ovm -m32 -w
sur un système d'exploitation compatible x86 POSIX.Exécuter avec
./vm < program
, oùprogram
est un fichier programme binaire.Aller pour la vitesse. Le programme effectue une traduction assez simple du programme d'entrée en code machine x86 et laisse le CPU faire le reste.
Par exemple, voici la traduction du programme de test.
ecx
,esi
Etedi
correspondent àR0
,R1
etR2
, respectivement;bh
détient les drapeaux d'état;eax
etedx
sont des registres à gratter; la pile d'appels correspond à la pile de la VM:Non golfé
Afficher l'extrait de code
la source
CJam,
222187185 octets * (trop lent / 2)Je voulais juste voir combien de temps je peux obtenir une machine virtuelle bytecode en l'écrivant dans CJam. Moins de 200 octets semble assez décent. C'est sacrément lent, car CJam lui-même est interprété. Il faut du temps pour exécuter le programme de test.
Pour l'exécuter, téléchargez l'interpréteur Java sur ce lien sourceforge , enregistrez le code
vm.cjam
et exécutez-le avecLe programme attend le bytecode sur STDIN. Je n'ai pas encore trouvé de moyen de transférer des données binaires dans un programme, sans que PowerShell ajoute un saut de ligne de fin et ne se convertisse
0x0a
en0x0d 0x0a
, ce qui est vraiment ennuyeux. Le code comprend 4 octets pour corriger cela (D-);
), que je n'ai pas inclus dans le nombre total, car ce n'est pas quelque chose que le programme devrait faire s'il a effectivement reçu le bytecode lui-même sur STDIN, au lieu d'une version étrangement encodée de celui-ci . Si quelqu'un connaît un correctif pour cela, faites-le moi savoir.Légèrement non golfé:
J'ajouterai une explication appropriée demain.
En bref, je stocke tous les registres, le pointeur d'instruction et l'indicateur de comparaison dans des variables, afin que je puisse garder la pile de CJam libre d'utiliser comme pile de la VM.
la source
python / c ++, score = 56,66
1435 caractères * .234 / 2 secondes * .5 [JIT] * .75 [Optimisation] * .90 [Instructions supplémentaires]
Compile le programme d'entrée en c ++, exécute gcc dessus, puis exécute le résultat. La plupart du temps est passé à l'intérieur de gcc.
La seule optimisation que je fais est de réduire les opérations de pile en variables explicites si cela est autorisé sémantiquement. Cela aide beaucoup, environ 10 fois mieux l'exécution du code compilé (environ 0,056 sec pour exécuter réellement le binaire résultant). Je ne sais pas ce que fait gcc qui vous apporte cette amélioration, mais c'est bien.
Pourrait certainement être joué au golf un peu plus.
la source
Lua 5.2 (ou LuaJIT), 740 octets
Premier essai, golf minimalement. Cette version fonctionne (au moins sur le programme de test) et implémente les opcodes supplémentaires, mais ne respecte pas l'exigence mathématique non signée et n'est pas particulièrement rapide. En bonus, cependant, c'est une machine virtuelle exécutée dans une machine virtuelle, et est écrite de telle sorte qu'elle peut être interprétée (exécutée avec PUC-Lua) ou sorte de JIT (exécutée avec LuaJIT; toujours interprétée, mais l'interpréteur est maintenant JITted).
EDIT: Golf mieux, toujours gros.
EDIT: correction d'une erreur majeure et restreignant désormais l'arithmétique à la
unsigned long
plage. D'une manière ou d'une autre, j'ai réussi à empêcher la taille de devenir incontrôlable, mais cela donne toujours la mauvaise réponse.EDIT: Il s'avère que le résultat était correct mais la sortie ne l'était pas. Passé à l'impression avec
%u
au lieu de%d
et tout va bien. Également désactivé les registres basés sur des tables pour les variables afin d'améliorer quelque peu la taille et la vitesse.EDIT: En utilisant la
goto
déclaration de Lua 5.2 (également disponible dans LuaJIT), j'ai remplacé l'interpréteur par "JIT-to-Lua", générant du code qui est exécuté directement par la machine virtuelle Lua elle-même. Je ne sais pas si cela compte vraiment comme JIT, mais cela améliore la vitesse.Voici la version originale et lisible.
la source
<
dans mes boucles au lieu de<=
, donc l'instruction finale de branchement a été laissée de côté. Il obtient toujours la mauvaise réponse, mais cela prend maintenant quelques minutes pour le faire. :)C #
15051475 octetsCeci est ma version de l'interpréteur, écrite en C # pourrait être optimisée / golfée plus je pense, mais je ne sais pas vraiment où;)
version golfée:
Éditer
supprimé certains inutiles
public
etprivate
modificateurs:appeler avec
executable.exe filename
où sefilename
trouve le fichier contenant le code à interpréterMon "programme de test":
Interprète non golfé avec des variables de noms plus longs, des classes, ...
la source