Bonjour,
J'ai une multiplication à faire par 1024. Il s'agit (ma bêtise a quand même ses limites) d'un entier long non signé. J'ai plusieurs solutions avec mon compilateur optimisé, resultat et valeur sont des uint16_t uint32_t:
A) resultat = valeur * 1024;
B) resultat = valeur << 10;
C) resultat = valeur *1023 +valeur;
Bien sûr il y en a d'autres, mon esprit inventif ne va pas vous faire la liste.
D’emblée comme ça, j'aurais tendance à dire que A est plus explicatif, mais que B est meilleur. Quand à C, c'est l'idée la plus stupide du jour (il n'est que 8h du matin).
MAIS, comme tout shadock qui se respecte, j'ai mesuré les temps de ces instructions (Maman m'a dit que si je mets la main dans une casserole d'eau bouillante, je vais me brûler. C'est vrai, j'ai vérifié). J'ai mesuré sur une Uno.
Pour B je trouve 86 cycles d'horloges. Comme mon entier long est coupé en 4 octets, on doit faire 10 fois un décalage de 1 case pour profiter de la retenue que l'on va passer au poids d'après. Il doit faire pour mon entier long non signé sur 4 octets A-B-C-D
Répéter 10 fois:
a) décale D de 1 case vers la gauche, ce qui dépasse va dans la retenue
b) décale C avec la retenue de 1 case vers la gauche, ce qui dépasse va dans la retenue
b) décale B avec la retenue de 1 case vers la gauche, ce qui dépasse va dans la retenue
b) décale A avec la retenue de 1 case vers la gauche
C'est tout à fait conforme à ce que j'attendais.
Pour A: faut pas prendre le compilateur pour un ministre, il voit bien que c'est une puissance de 2 et il va faire des décalages. On a donc aussi 86 cycles.
Pour C: rien que pour l'embêter, j'ai caché qu'on pouvait faire des décalages, et du coup il fait vraiment la multiplication. Ce qui durerait 64 cycles si il n'y avait pas l'addition, et ce qui fait 68 cycles avec
Conclusion: C'est bien la solution C la meilleure avec l'optimisation par défaut. Et si dans ma bibliothèque j'ai une multiplication par 1024 à faire, il vaut mieux cette formule.
Question: peut-on ne pas optimiser le code que pour une seule instruction?
Grand jeu concours: avec l'optimisation par défaut, trouvez la formule pour multiplier valeur par 1024 et mettre ceci dans resultat en utilisant le moins de cycles possible. Le gagnant gagne un karma.
Après cet exercice, j'ai quand même voulu en savoir plus. j'ai testé:
resultat = valeur << 32;
Il a pris 16 cycles. Je l'ai bien eu, il lui suffisait de faire
resultat = 0;
c'était pareil, et cela lui aurait coûté que 8 cycles.
Et si je testais les autres décalages pour resultat = valeur << N;
N | cycles | remarques |
---|---|---|
0 | 16 | c'est juste une copie |
1 | 20 | un seul décalage, pas de boucle |
2 | 30 | à partir d'ici, on fait des boucles et cela coûte 16+7N cycles |
3 | 37 | |
4 | 44 | |
... | ... | |
7 | 65 | |
8 | 72 | |
... | ... | |
15 | 121 | |
16 | 19 | pas idiot, c'est un décalage d'un seul octet |
17 | 135 | on reprend 16+7N cycles |
... | ... | |
22 | 171 | à partir d'ici 17+7N cycles; le cycle en plus vient-il d'une erreur de chronométrage? |
23 | 178 | |
24 | 20 | normal, c'est un décalage d'octets |
25 | 192 | reprise du 17+7N cycles |
... | ... | |
30 | 227 | 17+7N cycles |
31 | 22 | jun petit décalage à gauche pour récupérer le bit de poids fort... |
32 | 16 | peux mieux faire |
Je ne m'explique pas vraiment le 16+7N cycles qui passe à 17+7N cycles. Si je faisais une erreur de chronométrage sur les temps longs, pour N=24, je devrais retrouver 19 comme pour N=16!
Ils ont quand même optimisé pour N=16 et N=24, mais pas pour N=8.
Conclusion A partir de 8 décalages, il vaut mieux la solution C. Comme quoi il y a des idées saugrenues à garder.