Closed adud closed 6 years ago
Et tant qu'à faire, essayer de rassembler les opcodes par catégories ci-dessus
Une chose est sûre, vous ne pouvez pas faire ça dans l'interface mémoire parce que le désassembleur et le dump de la mémoire dans le debugger vont interférer.
À mon sens, le seul endroit correct pour faire ça c'est cpu.c
. Chaque instruction devrait compter elle-même la quantité de données lues. Mais c'est redondant parce que toutes les données passent déjà par disasm()
... qui ne peut pas compter parce qu'elle sert aussi pour le désassembleur.
Je pense qu'une solution correcte est la suivante :
cpu_execute()
utilise disasm_opcode()
avant d'exécuter l'instruction, et décide en fonction de la catégorie de l'instruction :
PC
en-dehors de la lecture de ses arguments [1]), alors cpu_execute()
ajoute la différence entre le nouveau et l'ancien PC
au total.disasm.c
public (ie. accessible via une fonction) pour connaître dynamiquement la longueur de chaque instruction est incontournable.push
, pop
, read
et autres write
, setctr
et getctr
comptent uniquement les bits qu'ils font transiter pour faire le job.J'espère que je n'ai pas oublié de bits dans mon calcul...
Pour les catégories, il y a déjà une catégorisation dans disasm.c
, vous pouvez l'étendre pour ajouter vos informations si vous voulez.
[1] Il faudra repasser sur la catégorisation de disasm.c
pour s'assurer que les fonctions catégorisées comme des sauts sont exactement celles qui modifient PC autrement que pour lire leurs opérandes.
Je suis en train d'implémenter une version légèrement différente :
jump
, jumpif
et setctr PC
retire la différence faite au PCEst-ce que ça vous semble correct ?
Alban
J'éviterais l'aspect « ajouter puis re-soustraire » si possible. À mon sens, si tu veux contourner le test par les catégories qui n'est en effet pas très élégant, tu peux t'en sortir en définissant une variable globale, que cpu_execute()
met à 1 par défaut, qui indique si le calcul du nombre de bits se fait par différence de PC
. Les instructions spéciales (setctr pc
, jumpif
, etc) peuvent mettre cette variable à 0 pour indiquer qu'elles font le calcul elles-mêmes.
Je viens de pusher une première version.
Elle utilise notamment la version « ajoute puis re-soustraire » pour compter les instructions.
Bref, je ferme l'issue pour le moment, mais je ne considère pas cette version comme définitive. Si vous avez la moindre remarque, n'hésitez pas à poster des messages ici. N'hésitez pas non plus à faire des modifications.
Enfin, j'ai pas encore énormément testé cette version, il se pourrait (j'espère pas) qu'il y ait des bugs. Dans l'idéal, il faudrait comparer avec ce qu'on a fait avec Antonin pour vérifier que les deux versions sont cohérentes.
Hum, peut-être aurais-tu dû tester ton code : notre version
memory interaction :
read :
total : 46548
ins 24916 53.5275%
dat 21248 45.6475%
scr 384 0.824955%
write :
total : 14464
dat 13440 92.9204%
scr 1024 7.07965%
La tienne
-----------------------------------------------
Exchanges between the Memory and the Processor:
-----------------------------------------------
Exchange Type | Bits Exchanged | Proportion
Instruction Read | 4294942504 | 100.0%
Memory Read | 21632 | 0.0%
Memory Write | 0 | 0.0%
Get/Set Counters | 13056 | 0.0%
Total | 4294977192 | 100.0%
Je ne te cache pas que je suis assez vexé par le fait que tes stats semblent indiquer que je n'en branle pas une étant donné que je n'écris jamais le résultat de ma matrice…
Oui, je viens de trouver deux erreurs :
Bref, d'autres tests à faire
Et on a ce qui ressemble à un underflow sur 32 bits, je regarde ça tout de suite.
Alban, pourrais-tu ajouter une option pour demander à l'émulateur de sortir les stats sous la formes:
mnemonic id_interne nb_d'occurences
.... .... .....
Pour faciliter le chargement des codages de huffmann :p
J'ai corrigé une erreur (j'avais oublié de compter les push
parmi les échanges ^^) et ajouter l'affichage du nombre d'instructions exécutées.
Après, j'ai fait un affiche très human readable. Si tu trouves qu'il est pénible à parser pour tes arbres de Huffmann, je peux ajouter une option qui permet de décider si l'affichage est lisible pour les humains ou pour les machines.
J'ai pas trop la foi de faire le python. En revanche il faudra que le python puisse sortir un fichier avec comme format: Un entier avec la longueur max d'un opcode Puis une ligne par instruction avec mnemonic id interne taille de l'opcopde opcode. Pour pouvoir charger facilement l'encodage dans l'émulateur au lancement.
Il faudra aussi améliorer l'assembleur pour pouvoir utiliser le même fichier pour assembler avec le bon encodage. Mais pour l'instant je m'occupe que du C
J'ai modifié un peu de documentation. Si jamais vous en avez la patience, essayez de respecter les conventions utilisées jusque-là dans le code :
/* */
C'est pas pour vous embêter, mais pour éviter que ça parte en bordel. C'est aussi une règle implicite dans les projets où des contributions externes ont lieu. ;)
J'ai modifié un décompte dans write
et push
, qui n'écrivent pas toujours 64 bits (sauf désaccord sur l'ISA, en tous cas je ne les ai pas implémentés comme ça) ; et je ne l'ai pas modifié mais je pense que getctr
est erroné puisque le processeur et la mémoire possèdent des versions synchronisées des compteurs ; ainsi en lecture il n'y a pas besoin de faire d'accès mémoire.
Edit : Je ne sais pas qui a fait un reformatage automatique de main.c
mais... c'était pas vraiment nécessaire. >_<
Bon, j'ai été obligé de ressortir une version de main.c
vieille de 8 commits pour annuler le reformatage automatique.
Veuillez ne pas refaire ça...
Il y a aussi des warnings très explicites sur le stack smashing que vous faites avec fscanf()
dans la fonction load_encoding()
. Soyez vigilants... j'ai commit des corrections. Ne laissez pas passer les warnings.
Pour le reformatage auto c'est moi: j'indente avec 4 espaces plutôt qu'avec des tabs (pour les raisons de différences de largeur des tab suivant les environnements) et mon éditeur a donc reformaté avec 4 espaces.
Juste pour dire merci du temps que vous y passez, que je suis grave sous l'eau pour une connerie de dépot de projet ANR jusqu'à mardi 13h, mais après je me mets à cet article, et même cela me fera des vacances. L'exploitateur
Je me rappelle qu'il restait un bug du genre underflow. Un commentaire sur sa résolution serait de bon goût au moment de fermer l'issue.
Est-ce que les call
et jump
ont bien été comptés comme envoyant une adresse 64 bits à la mémoire ? Ils sont équivalents à un setctr pc
, je crois.
L'underflow venait des return
, pour lesquels je comptais la différence au début et à la fin de l'instruction parmi la différence.
Voilà, donc si ça ne vous dérange pas, je peux refermer l'issue.
Juste avant, dans ma remarque précédente, j'ai oublié return
(alors que j'ai dit dans le premier commentaire de surtout pas les oublier…), qui vaut ici à une vache près setctr pc r7
, donc qui va aussi envoyer 64 bits sur le fil de données. Pour le push, c'est un peu plus tendu : tel quel, il faudrait qu'on le considère comme envoyant 64 bits à SP pour faire le pré-décrément, puis 1/4/8/16/32/64 pour les données
Remarque pertinente, je m'occuppe d'implémenter ça !
Le pré-décrément, par 64 bits ? Pas terrible ça... on a dupliqué les compteurs précisément pour éviter ce problème dans le cas du post-incrément.
Bon, à propos des échanges, il faut qu'on fixe des règles précises.
push
?jumpif
, je dirais bien qu'on envoie la valeur du nouveau PC si le saut a été effectué, 0 bits sinon. C'est bien comme ça que c'est censé se passer ?Rapidement...
push
reste à taille variable. Coût total : juste les données.Le 28/03/2018 à 19:38, Cemoixerestre a écrit :
Bon, à propos des échanges, il faut qu'on fixe des règles précises.
Est-ce qu'on compte des bits supplémentaires pour indiquer le nombre de bits mis sur la pile par |push| Pas compris la question. Push fait passer de P vers M les bits de 2 setctr et d'un write. Le size passe une fois de M vers P parce qu'il est dans l'instruction. Comme dans un write. Mais pas dans l'autre sens.
Est-ce qu'on échange 64 bits lorsque l'on change la valeur d'un compteur ? 32 seulement ? Autres suggestions ? P envoie les bits poids faible en premier, et s'arrête dès qu'il n'y a plus que des zéros. Autrement dit: P lève setcounter et met select à la bonne valeur pendant juste le nombre de cycles qu'il faut. Lorsque setcounter retombe, la mémoire complète l'adresse reçue avec des zéros dans les poids forts. (avantage non négligeable, cela rend certains de vous heureux puisque les adresses négatives sont tout à coup très chères)
Lorsque l'on fait un |jumpif|, je dirais bien qu'on envoie la valeur du nouveau PC si le saut a été effectué, 0 bits sinon. C'est bien comme ça que c'est censé se passer ? Oui.
Des instructions oubliées ? Des remarques ?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ens-lyon-2017/compass2018/issues/5#issuecomment-376972348, or mute the thread https://github.com/notifications/unsubscribe-auth/APlL7WrhZp7ufnryEuiaqaVkxrlUod6yks5ti8qXgaJpZM4Svq8j.
Alban, si ton code est assez souple, tu saurais-tu compiler plusieurs versions avec différentes manières de compter ? Je suis parti dans le sujet d'une version vraiment naïve (donc avec beaucoup de surcoût inutile, comme : « Que se passe-t-il si on ne dédouble pas les compteurs en mémoire ? ») à quelques améliorations qu'on pourrait faire sur notre processeur. J'ai aussi un petit « 8, ça suffit », à propos des registres.
Bon ok, j'ai ajouté le morceau d'article, si c'est ok pour tous, il faudra faire des stats avec au moins la version naïve et la version la plus efficace (bon ok, sans incctr
et decctr
), mais dans l'idéal avec pas mal d'intermédiaires, pour voir ce qui est vraiment efficace, moyennement efficace, tout pourri, et les indiquer dans l'article.
Pour avoir de bonnes stats, il serait de bon ton de calculer précisément chaque bit échangé. Alban et moi avions commencé sur notre proco, mais finalement j'avais oublié juste beaucoup trop de trucs. Dans l'idée on aurait besoin de distinguer :
readze xx 32
coûte par exemple 32 bits)setctr cc rx
coûte 64 bits pour verser la valeur derx
danscc
). Ne pas oubliercall
,return
,jump
etpush