Le mot clé ressortingct offre-t-il des avantages significatifs en gcc / g ++?

Quelqu’un a-t-il vu des chiffres / parsings pour savoir si l’utilisation du mot-clé ressortingct C / C ++ dans gcc / g ++ actual fournit une amélioration significative des performances dans la réalité (et pas seulement en théorie)?

J’ai lu divers articles recommandant / dénigrant son utilisation, mais je n’ai pas rencontré de nombres réels démontrant pratiquement des arguments des deux côtés.

MODIFIER

Je sais que cette ressortingct ne fait pas officiellement partie de C ++, mais elle est prise en charge par certains compilateurs et j’ai lu un article de Christer Ericson qui recommande fortement son utilisation.

Le mot-clé ressortingct fait une différence.

J’ai vu des améliorations du facteur 2 et plus dans certaines situations (traitement d’images). La plupart du temps, la différence n’est pas si importante. Environ 10%.

Voici un petit exemple qui illustre la différence. J’ai écrit une très simple transformation de masortingce de vecteur 4×4 en tant que test. Notez que je dois forcer la fonction à ne pas être insérée. Sinon, GCC détecte qu’il n’y a pas de pointeurs d’alias dans mon code de référence et restreint ne ferait aucune différence en raison de l’inline.

J’aurais également pu déplacer la fonction de transformation dans un autre fichier.

 #include  #ifdef USE_RESTRICT #else #define __ressortingct #endif void transform (float * __ressortingct dest, float * __ressortingct src, float * __ressortingct masortingx, int n) __atsortingbute__ ((noinline)); void transform (float * __ressortingct dest, float * __ressortingct src, float * __ressortingct masortingx, int n) { int i; // simple transform loop. // written with aliasing in mind. dest, src and masortingx // are potentially aliasing, so the comstackr is forced to reload // the values of masortingx and src for each iteration. for (i=0; i 

Résultats: (sur mon Core Duo 2 Ghz)

 nils@doofnase:~$ gcc -O3 test.c nils@doofnase:~$ time ./a.out real 0m2.517s user 0m2.516s sys 0m0.004s nils@doofnase:~$ gcc -O3 -DUSE_RESTRICT test.c nils@doofnase:~$ time ./a.out real 0m2.034s user 0m2.028s sys 0m0.000s 

Sur le pouce, exécution 20% plus rapide sur ce système.

Pour montrer à quel point cela dépend de l'architecture, j'ai laissé le même code s'exécuter sur un processeur Cortex-A8 intégré (le nombre de boucles a été ajusté car je ne veux pas attendre trop longtemps):

 root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp test.c root@beagleboard:~# time ./a.out real 0m 7.64s user 0m 7.62s sys 0m 0.00s root@beagleboard:~# gcc -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -DUSE_RESTRICT test.c root@beagleboard:~# time ./a.out real 0m 7.00s user 0m 6.98s sys 0m 0.00s 

Ici, la différence n'est que de 9% (même compilateur btw.)

L’article Démystifier le mot-clé de ressortingction fait référence à l’article Pourquoi l’aliasing spécifié par le programmeur est une mauvaise idée (pdf) qui indique que cela n’aide généralement pas et fournit des mesures pour le sauvegarder.

Le mot clé ressortingct offre-t-il des avantages significatifs en gcc / g ++?

Cela peut réduire le nombre d’instructions, comme indiqué dans l’exemple ci-dessous, donc utilisez-le autant que possible.

Exemple de GCC 4.8 Linux x86-64

Consortingbution:

 void f(int *a, int *b, int *x) { *a += *x; *b += *x; } void fr(int *ressortingct a, int *ressortingct b, int *ressortingct x) { *a += *x; *b += *x; } 

Comstackr et décomstackr:

 gcc -g -std=c99 -O0 -c main.c objdump -S main.o 

Avec -O0 , ils sont identiques.

Avec -O3 :

 void f(int *a, int *b, int *x) { *a += *x; 0: 8b 02 mov (%rdx),%eax 2: 01 07 add %eax,(%rdi) *b += *x; 4: 8b 02 mov (%rdx),%eax 6: 01 06 add %eax,(%rsi) void fr(int *ressortingct a, int *ressortingct b, int *ressortingct x) { *a += *x; 10: 8b 02 mov (%rdx),%eax 12: 01 07 add %eax,(%rdi) *b += *x; 14: 01 06 add %eax,(%rsi) 

Pour les non-initiés, la convention d’appel est la suivante:

  • rdi = premier paramètre
  • rsi = second paramètre
  • rdx = troisième paramètre

Conclusion: 3 instructions au lieu de 4 .

Bien sûr, les instructions peuvent avoir différentes latences , mais cela donne une bonne idée.

Pourquoi GCC a pu optimiser cela?

Le code ci-dessus est tiré de l’exemple de Wikipedia qui est très éclairant.

Pseudo-assemblage pour f :

 load R1 ← *x ; Load the value of x pointer load R2 ← *a ; Load the value of a pointer add R2 += R1 ; Perform Addition set R2 → *a ; Update the value of a pointer ; Similarly for b, note that x is loaded twice, ; because a may be equal to x. load R1 ← *x load R2 ← *b add R2 += R1 set R2 → *b 

Pour fr :

 load R1 ← *x load R2 ← *a add R2 += R1 set R2 → *a ; Note that x is not reloaded, ; because the comstackr knows it is unchanged ; load R1 ← *x load R2 ← *b add R2 += R1 set R2 → *b 

Est-ce vraiment plus rapide?

Ermmm … pas pour ce simple test:

 .text .global _start _start: mov $0x10000000, %rbx mov $x, %rdx mov $x, %rdi mov $x, %rsi loop: # START of interesting block mov (%rdx),%eax add %eax,(%rdi) mov (%rdx),%eax # Comment out this line. add %eax,(%rsi) # END ------------------------ dec %rbx cmp $0, %rbx jnz loop mov $60, %rax mov $0, %rdi syscall .data x: .int 0 

Et alors:

 as -o ao aS && ld ao && time ./a.out 

sur Ubuntu 14.04 AMD64 CPU Intel i5-3210M.

J’avoue que je ne comprends toujours pas les processeurs modernes. Fais moi savoir si tu:

  • trouvé une faille dans ma méthode
  • trouvé un cas de test d’assembleur où il devient beaucoup plus rapide
  • comprendre pourquoi il n’y avait pas de différence

J’ai testé ce programme. Sans ressortingct il a fallu 12,640 secondes pour terminer, avec 12,516 ressortingct . On dirait que ça peut faire gagner du temps.

Notez que les compilateurs C ++ qui autorisent le mot-clé ressortingct peuvent toujours l’ignorer. C’est le cas par exemple ici .