Je veux écrire un firmware de code C pour les microcontrôleurs Atmel AVR. Je vais le compiler en utilisant GCC. Je souhaite également activer les optimisations du compilateur (-Os
ou -O2
), car je ne vois aucune raison de ne pas les activer, et ils généreront probablement un meilleur assemblage plus rapidement que l'écriture manuelle de l'assemblage.
Mais je veux un petit morceau de code non optimisé. Je veux retarder l'exécution d'une fonction d'un certain temps, et donc je voulais écrire une boucle de ne rien faire juste pour perdre du temps. Pas besoin d'être précis, attendez un peu.
/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i);
}
Étant donné que l'accès à la mémoire dans AVR est beaucoup plus lent, je souhaite que i
et j
soient conservés dans les registres du processeur.
Mise à jour: je viens de trouver til/delay.h et til/delay_basic.h de AVR Libc . Bien que la plupart du temps, il soit préférable d'utiliser ces fonctions, cette question reste valable et intéressante.
Questions connexes:
J'ai développé cette réponse après avoir suivi un lien de réponse de dmckee , mais cela prend une approche différente de sa réponse.
Attributs de fonction la documentation de GCC mentionne:
noinline
Cet attribut de fonction empêche la prise en compte d'une fonction pour l'inline. Si la fonction n'a pas d'effets secondaires, il existe des optimisations autres que l'inline qui entraînent l'optimisation des appels de fonction, bien que l'appel de fonction soit en direct. Pour éviter que ces appels ne soient optimisés, mettezasm ("");
Cela m'a donné une idée intéressante ... Au lieu d'ajouter une instruction nop
à la boucle interne, j'ai essayé d'ajouter un code d'assemblage vide là-dedans, comme ceci:
unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i)
asm("");
}
Et ça a marché! Cette boucle n'a pas été optimisée et aucune instruction nop
supplémentaire n'a été insérée.
De plus, si vous utilisez volatile
, gcc stockera ces variables dans RAM et ajoutera un tas de ldd
et std
pour les copier aux registres temporaires. Cette approche, en revanche, n'utilise pas volatile
et ne génère pas de surcharge.
Mise à jour: Si vous compilez du code en utilisant -ansi
Ou -std
, Vous devez remplacer le asm
mot-clé avec __asm__
, comme décrit dans la documentation GCC .
De plus, vous pouvez également utiliser __asm__ __volatile__("")
si votre instruction Assembly doit s'exécuter là où nous la plaçons (c'est-à-dire qu'elle ne doit pas être déplacée hors d'une boucle en tant que optimisation) .
Déclarez les variables i
et j
comme volatile
. Cela empêchera le compilateur d'optimiser le code impliquant ces variables.
unsigned volatile char i, j;
Je ne sais pas pourquoi il n'a pas encore été mentionné que cette approche est complètement erronée et facilement brisée par les mises à niveau du compilateur, etc. jusqu'à ce que la valeur souhaitée soit dépassée. Sur x86, vous pouvez utiliser rdtsc
à cet effet, mais le moyen le plus portable serait d'appeler clock_gettime
(ou la variante pour votre système d'exploitation non-POSIX) pour obtenir l'heure. Linux x86_64 actuel évitera même l'appel système pour clock_gettime
et utilisez rdtsc
en interne. Ou, si vous pouvez gérer le coût d'un appel système, utilisez simplement clock_nanosleep
pour commencer...
Je ne sais pas du haut de ma tête si la version avr du compilateur prend en charge le ensemble complet de #pragma
s (les plus intéressants dans le lien datent tous de gcc version 4.4), mais c'est là que vous commenceriez habituellement.
Pour moi, sur GCC 4.7.0, asm vide a été optimisé de toute façon avec -O3 (n'a pas essayé avec -O2). et l'utilisation d'un i ++ dans le registre ou volatile a entraîné une grosse pénalité de performance (dans mon cas).
Ce que j'ai fait était de créer un lien avec une autre fonction vide que le compilateur ne pouvait pas voir lors de la compilation du "programme principal"
Fondamentalement, cela:
Création de "helper.c" avec cette fonction déclarée (fonction vide)
void donotoptimize(){}
Puis compilé "gcc helper.c -c -o helper.o" puis
while (...) { donotoptimize();}
Cela m'a donné les meilleurs résultats (et d'après moi, pas de frais généraux, mais ne peut pas tester car mon programme ne fonctionnera pas sans cela :))
Je pense que cela devrait aussi fonctionner avec icc. Peut-être pas si vous activez les optimisations de liaison, mais avec gcc, c'est le cas.
Mettre asm volatile devrait aider. Vous pouvez en savoir plus à ce sujet ici: -
http://www.nongnu.org/avr-libc/user-manual/optimization.html
Si vous travaillez sous Windows, vous pouvez même essayer de mettre le code sous pragmas, comme expliqué en détail ci-dessous: -
J'espère que cela t'aides.
placez cette boucle dans un fichier .c séparé et n'optimisez pas ce fichier. Mieux encore, écrivez cette routine dans l'assembleur et appelez-la depuis C, de toute façon l'optimiseur ne s'impliquera pas.
Je fais parfois la chose volatile mais je crée normalement une fonction asm qui renvoie simplement un appel à cette fonction, l'optimiseur rendra la boucle for/while serrée, mais il ne l'optimisera pas car il doit faire tous les appels à la fonction factice. La réponse nop de Denilson Sá fait la même chose mais encore plus serrée ...