Comment empêcher GCC d'optimiser une boucle d'attente chargée?
je veux écrire un firmware de code C pour les microcontrôleurs AVR D'Atmel. Je le compilerai en utilisant GCC. En outre, je veux activer les optimisations des compilateurs ( -Os
ou -O2
), car je ne vois aucune raison de ne pas les activer, et ils généreront probablement un meilleur assemblage beaucoup plus rapidement que l'écriture manuelle.
, Mais je veux un petit morceau de code non optimisé. Je veux retarder l'exécution d'une fonction en un certain temps, et donc j'ai voulu écrire une ne rien faire boucle juste pour perdre quelques temps. Pas besoin d'être précis, juste attendre un peu.
/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i);
}
comme L'accès à la mémoire dans AVR est beaucoup plus lent, je veux que i
et j
soient conservés dans les registres CPU.
mise à jour: je viens de trouver util/delay.h et util/delay_basic.h à partir de "1519180920 AVR Libc . Bien que la plupart du temps, il pourrait être une meilleure idée d'utiliser ces cette question reste valable et intéressante.
questions connexes:
- comment empêcher gcc d'optimiser certaines déclarations en C?
- Existe-t-il un moyen de dire à GCC de ne pas optimiser un morceau de code particulier?
- comment ne pas optimiser à l'extérieur-mécanique d'une fonction de folie
8 réponses
j'ai développé cette réponse après avoir suivi un lien à partir de réponse de dmckee , mais il prend une approche différente de sa réponse.
attributs de fonction documentation de GCC mentions:
noinline
Cet attribut de fonction empêche une fonction d'être considérée pour l'inclinaison. Si la fonction n'a pas d'effets secondaires, il y a des optimisations autres que l'incrustation qui fait que les appels de fonction sont optimisés à l'écart, bien que l'appel de fonction soit en direct. Pour éviter que de tels appels ne soient optimisés, mettezasm ("");
Cela m'a donné une idée intéressante... Au lieu d'ajouter une instruction nop
à la boucle intérieure, j'ai essayé d'y ajouter un code d'assemblage vide, 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 supplémentaire nop
ont été insérés.
de plus , si vous utilisez volatile
, gcc stockera ces variables en RAM et ajoutera un paquet de ldd
et std
pour les copier dans des registres temporaires. Cette approche, d'autre part, n'utilise pas volatile
et ne génère pas de tels frais généraux.
mise à jour: si vous compilez le code en utilisant -ansi
ou -std
, vous devez remplacer le asm
mot-clé avec __asm__
, comme décrit dans la documentation de GCC .
en outre, vous pouvez également utiliser __asm__ __volatile__("")
si votre déclaration de l'assemblage doit exécuter où nous le mettons, (c.-à-d. ne doit pas être déplacé hors d'une boucle comme une optimisation) .
déclare i
et j
variables comme volatile
. Cela empêchera le compilateur d'optimiser le code impliquant ces variables.
unsigned volatile char i, j;
Je ne suis pas sûr pourquoi il n'a pas été mentionné encore que cette approche est complètement mal orientée et facilement cassé par des mises à niveau de compilateur, etc. Il serait beaucoup plus logique pour déterminer la valeur de temps vous voulez attendre jusqu'à ce spin et d'interrogation à l'heure actuelle jusqu'à ce que la valeur souhaitée soit atteinte. Sur x86, Vous pouvez utiliser rdtsc
à cette fin, mais le moyen le plus portable serait d'appeler clock_gettime
(ou la variante pour votre OS non-POSIX) pour obtenir le temps. Linux x86_64 actuel va même évitez le système clock_gettime
et utilisez rdtsc
en interne. Ou, si vous pouvez gérer le coût d'un syscall, il suffit d'utiliser clock_nanosleep
pour commencer...
Je ne sais pas si la version avr du compilateur supporte le jeu complet de #pragma
s (les intéressants dans le lien datent tous de la version 4.4 de gcc), 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 register ou volatile a entraîné une grosse pénalité de performance (dans mon cas).
ce que j'ai fait était de lier avec une autre fonction vide que le compilateur ne pouvait pas voir lors de la compilation du" programme principal "
essentiellement ceci:
helper " Created".c" avec cette fonction déclarée (fonction vide)
void donotoptimize(){}
puis compilé " gcc helper.C-C-O helper.o" et puis
while (...) { donotoptimize();}
cela m'a donné les meilleurs résultats (et de ma croyance, pas de frais généraux du tout, mais ne peut pas tester parce que mon programme ne fonctionnera pas sans elle :))
je pense que cela devrait fonctionner avec la cpi. Peut-être pas si vous activez les optimisations de liens, mais avec gcc si.
mettez cette boucle à part .C et ne pas optimiser ce seul fichier. Encore mieux écrire cette routine en assembleur et l'appeler à partir de C, de toute façon l'optimiseur ne sera pas impliqué.
je fais parfois la chose volatile mais normalement créer une fonction asm qui retourne simplement l'optimiseur rend la boucle for/while serrée mais ne l'optimise pas car il doit faire tous les appels à la fonction dummy. La réponse de nop Denilson Sá fait la même chose, mais encore plus serré...
mettre volatile asm devrait aider. Vous pouvez lire plus sur ce ici: -
http://www.nongnu.org/avr-libc/user-manual/optimization.html
si vous travaillez sur Windows, vous pouvez même essayer de mettre le code sous pragmas, comme expliqué en détail ci-dessous: -
Espérons que cette aide.
Vous pouvez également utiliser la fonction s'inscrire mot-clé . Les Variables déclarées avec le Registre sont stockées dans les registres CPU.
dans votre cas:
register unsigned char i, j;
j = 0;
while(--j) {
i = 0;
while(--i);
}