Go Down

Topic: Voir le code assembleur (Read 11493 times) previous topic - next topic

Super_Cinci

#15
Feb 21, 2012, 07:47 am Last Edit: Feb 21, 2012, 07:50 am by Super_Cinci Reason: 1
J'ai beaucoup "travaillé" sur l'optimisation de code arduino, mais finalement, j'utilise au max les registres et au minimum les fonctions arduino. il suffit de compiler ces deux codes. il font exactement la même chose : faire clignoter le plus rapidement possible la led 13.
(J'ai compilé pour une méga 2560, c'est ce que j'avais sous la main)
Code: [Select]

void setup() {
  pinMode (13, OUTPUT);
}

void loop() {
  digitalWrite(13, !digitalRead(13));
}

Compilation : 1486 octets
INTERPRETATION :

t = 5µs / div

Temps d'exécution de loop() : 14µs, mais de temps en temps 20µs comme en témoignent la partie haute du signal qui se prolonge sur 4 div (moins illuminée). Cette "prolongation" n'intervient que sur une mise à 0 de pin13 (why?).
Nombre de cycles occupés par la fonction loop() : 224
Fréquence en sortie pin 13 : 35,715KHz, 29,412KHz quand on a une prolongation.
Je n'ai pas trop cherché à trouver la récurrence de cette prolongation parasite, mais cela semble bien régulier. Il est possible qu'elle provienne de l'ISR du timer 0 (pour les variables des fonctions millis() et micros()). Cette prolongation représenterait environ 96 cycles d'horloge.


Code: [Select]

void setup() {
  DDRB |= 0x80;   // B7 en sortie (pin 13 de la méga)
}

void loop() {
  PORTB ^= 0x80;   // ou exclusif : inverse le bit 7 de PORTB
}

Compilation : 670 octets
INTERPRETATION :

t = 0,5µs / div

Temps d'exécution de loop() : 0,92µs, on n'observe pas de prolongation.
Nombre de cycles occupés par la fonction loop() : 15
Fréquence en sortie pin 13 : 533,33KHz
De petites oscillations sont visibles (environ 32MHz, le double du quartz, mais elles viennent peut-être de l'oscilo, à cette vitesse, les mesures ne sont peut-être pas fiables, l'oscillo est donné pour 40MHz max). on a aussi un signal périodique parasite à 5.55MHz mais qui doit aussi provenir de l'oscillo ou l'association des capacités internes de l'arduino, de la led 13 et de l'oscillo.

Conclusion : On gagne un rapport 2,22 en taille de programme, et un rapport entre 15 et 17 sur la rapidité d'exécution. C'est déjà une belle avancée. Bien sûr, côté programmation, un digitalWrite(pin_num, value) est bien plus souple qu'un PORTx |= value ou PORTx &= value, mais si on a vraiment besoin de rapidité, alors ça vaut le coup d'y réfléchir.

Pour ce qui est de l'utilisation de la lib timer2ms, je la déconseille fortement, on en avait déjà parlé dans un topic de 68tjs. Cette librairie est très lourde de code et la valeur envoyée en tant qu'intervalle de timer n'est pas du tout vérifiée en sortie. En deux ou trois instructions, on renseigne les registres du timer, et c'est mille fois plus rapide et d'une précision sans faute (il en va de même pour les servos, la lib servo n'est compatible avec aucune autre fonction avancée, le servo se met à trembler).

Je crois qu'un topic regroupant les optimisations simples serait utile...

MiGaNuTs

Merci a tous, c'est tout a fait le genre de choses que je voulais voir/savoir.

Dans la majeure partie des cas les fonctions "toutes faites" d'arduino font parfaitement l'affaire, mais c'est toujours intéressant et parfois utile de pouvoir aller plus loin.


Bonne idée ce topic sur les optimisations !
(C'est un sujet sans fin, la chose risque d'être assez compliquée en fait.)

pledoux

C'est interessant void le code en assembly pour apprendre a lui utilisé.

C'est a dire que en plusieurs problémes une bonne chose est faire le programme
principal en langage de haut niveau qui apelle une function en assembly.

Au cas du arduino est-que quelqu'un ici a melangé le code en langague Arduino
et assembly? Est-que utilisé asm() est une bonne solution?

Artouste


J'ai beaucoup "travaillé" sur l'optimisation de code arduino, mais finalement, j'utilise au max les registres et au minimum les fonctions arduino. il suffit de compiler ces deux codes. il font exactement la même chose : faire clignoter le plus rapidement possible la led 13.
(J'ai compilé pour une méga 2560, c'est ce que j'avais sous la main)


Je crois qu'un topic regroupant les optimisations simples serait utile...


démo très intéressante et bien illustrée avec un bon vieil oscillo analogique  :smiley-mr-green:

osaka

#19
Feb 21, 2012, 09:34 pm Last Edit: Feb 22, 2012, 12:48 am by osaka Reason: 1

Au cas du arduino est-que quelqu'un ici a melangé le code en langague Arduino
et assembly? Est-que utilisé asm() est une bonne solution?


Dans la lib avr on en rencontre souvent, il suffit d'une simple directive au compilateur.

Code: [Select]

__asm__ __volatile__ ();


voir util/crc16.h par exemple:

Code: [Select]

/** \ingroup util_crc
    Optimized CRC-16 calculation.

    Polynomial: x^16 + x^15 + x^2 + 1 (0xa001)<br>
    Initial value: 0xffff

    This CRC is normally used in disk-drive controllers.

    The following is the equivalent functionality written in C.

    \code
    uint16_t
    crc16_update(uint16_t crc, uint8_t a)
    {
int i;

crc ^= a;
for (i = 0; i < 8; ++i)
{
    if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
    else
crc = (crc >> 1);
}

return crc;
    }

    \endcode */

static __inline__ uint16_t
_crc16_update(uint16_t __crc, uint8_t __data)
{
uint8_t __tmp;
uint16_t __ret;

__asm__ __volatile__ (
"eor %A0,%2" "\n\t"
"mov %1,%A0" "\n\t"
"swap %1" "\n\t"
"eor %1,%A0" "\n\t"
"mov __tmp_reg__,%1" "\n\t"
"lsr %1" "\n\t"
"lsr %1" "\n\t"
"eor %1,__tmp_reg__" "\n\t"
"mov __tmp_reg__,%1" "\n\t"
"lsr %1" "\n\t"
"eor %1,__tmp_reg__" "\n\t"
"andi %1,0x07" "\n\t"
"mov __tmp_reg__,%A0" "\n\t"
"mov %A0,%B0" "\n\t"
"lsr %1" "\n\t"
"ror __tmp_reg__" "\n\t"
"ror %1" "\n\t"
"mov %B0,__tmp_reg__" "\n\t"
"eor %A0,%1" "\n\t"
"lsr __tmp_reg__" "\n\t"
"ror %1" "\n\t"
"eor %B0,__tmp_reg__" "\n\t"
"eor %A0,%1"
: "=r" (__ret), "=d" (__tmp)
: "r" (__data), "0" (__crc)
: "r0"
);
return __ret;
}

pledoux

Quand s'utilise le avr-gcc pour faire la compilation du code en langage C pour le AVR au lieu
de l'IDE du Arduino est possible regarder le code generé en assembly quand on utilise le command
-S au prompt. Par example

root@pedro-Vostro-1014:/home/pedro# avr-gcc -S -Os -mmcu=atmega168 -o teste teste.c

J'ai fait ça en utilisé çe terminal du Linux en command de prompt.
Donc le text du code traduit au assembly a apparu en mon fichier.

La IDE du Arduino utilise le avr-gcc. Je ne sais pas comme tourné cette option du
compilateur a l'IDE du Arduino  :(

osaka


Tien cette sortie console m'a permis de trouver quelque chose d'interessant -> "Compiler.java" dans le source de l'ide arduino.

Et la réponse à ma question pour make ou ant ...

Code: [Select]

 static private List getCommandCompilerS(String avrBasePath, List includePaths,
   String sourceName, String objectName, Map<String, String> boardPreferences) {
   List baseCommandCompiler = new ArrayList(Arrays.asList(new String[] {
     avrBasePath + "avr-gcc",
     "-c", // compile, don't link
     "-g", // include debugging info (so errors include line numbers)
     "-assembler-with-cpp",
     "-mmcu=" + boardPreferences.get("build.mcu"),
     "-DF_CPU=" + boardPreferences.get("build.f_cpu"),
     "-DARDUINO=" + Base.REVISION,
   }));

   List baseCommandCompiler = new ArrayList(Arrays.asList(new String[] {
     avrBasePath + "avr-gcc",
     "-c", // compile, don't link
     "-g", // include debugging info (so errors include line numbers)
     "-Os", // optimize for size
     Preferences.getBoolean("build.verbose") ? "-Wall" : "-w", // show warnings if verbose
     "-ffunction-sections", // place each function in its own section
     "-fdata-sections",
     "-mmcu=" + boardPreferences.get("build.mcu"),
     "-DF_CPU=" + boardPreferences.get("build.f_cpu"),
     "-MMD", // output dependancy info
     "-DARDUINO=" + Base.REVISION,
   }));

  List baseCommandCompilerCPP = new ArrayList(Arrays.asList(new String[] {
     avrBasePath + "avr-g++",
     "-c", // compile, don't link
     "-g", // include debugging info (so errors include line numbers)
     "-Os", // optimize for size
     Preferences.getBoolean("build.verbose") ? "-Wall" : "-w", // show warnings if verbose
     "-fno-exceptions",
     "-ffunction-sections", // place each function in its own section
     "-fdata-sections",
     "-mmcu=" + boardPreferences.get("build.mcu"),
     "-DF_CPU=" + boardPreferences.get("build.f_cpu"),
     "-MMD", // output dependancy info
     "-DARDUINO=" + Base.REVISION,
   }));




Peut être modifier ceci et recréer l'archive core.jar (s'il est bien dans celui-ci) ?

pledoux

Est-que utilisé l'IDE du arduino est une bonne option?

J'ai fait un programme que fais la même chose en C et a la langague du arduino.
en C j'ai 508 bytes contre 990 de l'IDE du arduino. Le .hex du programe fait en C
est tres plus petit.

skywodd


Est-que utilisé l'IDE du arduino est une bonne option?

J'ai fait un programme que fais la même chose en C et a la langague du arduino.
en C j'ai 508 bytes contre 990 de l'IDE du arduino. Le .hex du programe fait en C
est tres plus petit.

L'ide arduino ajoute les tables de correspondance broches <> numéro qui font ~440 octets
508 + 440 = 948, + options d'optimisation ~= 990 ... le compte est bon.

Le core arduino, est (je le répète h24 :smiley-mr-green:) concu pour être facile d'utilisation, mais pas pour être optimisé !
Si on veut faire des choses optimisé il faut utiliser du c/c++ pure sans surcouche ;)

@osaka:
Quote
Peut être modifier ceci et recréer l'archive core.jar (s'il est bien dans celui-ci) ?

Si tu compte recompiler le .jar de l'ide tu va galérer, crois moi j'ai essayé, entre la lib processing, rxtx, les trucs immondes dans le code ... et la nouvelle version de java 7 c'est une pure galère de faire un .jar identique à celui d'origine ...

Astuce: renomme avr-gcc et crée un fichier batch (.bat ou .sh sous linux) avec comme nom avr-gcc, l'ide appelera ton batch qui lui appelera ensuite le compilateur, mais tu pourra modifier les arguments au passage ;)
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

pledoux

L'IDE du Arduino et sa langague vraimment fais des choses plus facile quand on veux
utilisé autres dispositif attaché au controllateur comme le LCD, Ethernet etc.

Quand on veux faire une chose plus optimisé c'est a dire que on doit en faire en assembly.

L'architecture AVR a eté fait pour être simple pour le compilateur quand se traduire le code d'une
language de haut niveau au assembly. Donc je ne sais pas si il aura quelque grande difference
de optimisation entre ecrire en C ou Assembly.

.Le même n'est pas vrai sur le PIC. Donc le AVR n'ai pas
bensoin de beaucoup d'ajustation au compilateur comme le PIC. Donc le PIC a plusieurs compilateurs
commerciel pour optimisé ça. la vantage du PIC est que son assembly est tres simple, seulement 35
instrutions.

au fin la vantage rassemble d'etre bonne par l'AVR
http://embeddedgurus.com/stack-overflow/2009/12/hardware-costs-versus-development-costs/

Super_Cinci

en essayant de comprendre le bootloader, j'y ai lu ceci :

Quote

   /* A bunch of if...else if... gives smaller code than switch...case ! */
ça peut être bon à savoir...

osaka

#26
Feb 23, 2012, 11:43 pm Last Edit: Feb 23, 2012, 11:45 pm by osaka Reason: 1

L'ide arduino ajoute les tables de correspondance broches <> numéro qui font ~440 octets
508 + 440 = 948, + options d'optimisation ~= 990 ... le compte est bon.


Je viens de faire un test en virant tout, j'ai d'abord commencé par tout ce qui était lié à cette correspondance donc pins_arduino et ses tableaux en progmem et tout ce qui en découle , rien ne change toujours 450 octets minimum (d'un côté c'est normal si aucun appel n'est fait à ceux ci  :smiley-sweat:).
La première chose qui m'a permis de diminué cette taille à été la méthode init() (celle appelé avant tout autre chose) dans wiring , 170 octets en moins (mais reste obligatoire au bon fonctionnement si je comprend bien ?).
Après j'ai supprimer l'intégralité même l'appel de setup et loop reste 178 octets (510 pour la mega) de je ne sais où , il n'y a plus que l'include de WProgram.h obligatoire par l'ide apparemment mais qui a été vidé intégralement également.
Enfin tout ça n'a peut être rien à voir, comme je suis pas un pro du microcontroleur.  XD


Si tu compte recompiler le .jar de l'ide tu va galérer, crois moi j'ai essayé, entre la lib processing, rxtx, les trucs immondes dans le code ... et la nouvelle version de java 7 c'est une pure galère de faire un .jar identique à celui d'origine ...


J'ai regardé un coup dans l'archive (pde.jar) pour voir le manifet et rien de spécial il est vide, normalement juste compilé la classe concernée et reconstruire l'archive devrait suffire ?
Par contre oui le code il faut s'amuser et ce balader un peux partout pour retrouvé ses jeunes, si on veux une modif en profondeur (modifications de méthodes de classes) c'est pas gagné  :~


Astuce: renomme avr-gcc et crée un fichier batch (.bat ou .sh sous linux) avec comme nom avr-gcc, l'ide appelera ton batch qui lui appelera ensuite le compilateur, mais tu pourra modifier les arguments au passage ;)


Pas bête, j'y avais pas pensé, enfin le jours ou j'aurais besoin de le faire c'est que je me passerai de l'ide arduino également.  :smiley-mr-green:


en essayant de comprendre le bootloader, j'y ai lu ceci :

Quote

   /* A bunch of if...else if... gives smaller code than switch...case ! */
ça peut être bon à savoir...


Je sais plus qui avait fais le test une fois il n'y a pas si longtemps et la différence n'était pas énorme il me semble ou c'était niveau process je sais plus ?

Benvenuto

En l'occurrence il s'agit d'une série de if .. else if avec des dizaines de cas, et comme c'est le bootloader ils en sont vraiment à l'octet près en termes de taille du programme  :)

Super_Cinci

Mais justement, c'est plutôt étonnant qu'un...
Code: [Select]

if(){
  } else if() {
  } else if() {
  } else if() {
  } else if() {
  } else {
}

soit mieux encodé qu'un...
Code: [Select]

switch(){
  case ...:
    break;
  case ...:
    break;
  case ...:
    break;
  case ...:
    break;
  default:
    break;
}
puisqu'ils font exactement la même chose à part que dans le else if, on peut faire des tests complètement indépendants, alors que dans un switch(), on teste toujours la même variable,  et pas besoin de recharger un registre avec la valeur à comparer (le switch devrait donc être plus léger. C'est un peu hors de bon sens à mon avis, d'où l'utilité de faire de l'assembleur dans un code où l'optimisation est nécessaire au plus haut point.

Go Up