502 votes

Pourquoi gcc génère un code plus rapide 15-20 % si j’optimise pour la taille au lieu de vitesse ?

J'ai d'abord remarqué en 2009 que gcc (au moins sur mes projets et sur mes machines) ont tendance à générer beaucoup plus rapidement code si j'optimiser pour la taille (-Os) au lieu de la vitesse (-O2 ou -O3) et j'ai été vous demandez-vous jamais pourquoi depuis.

J'ai réussi à créer un (plutôt ridicule) de code qui montre ce comportement surprenant et est suffisamment petit pour être posté ici.

const int LOOP_BOUND = 200000000;

__attribute__((noinline))
static int add(const int& x, const int& y) {
    return x + y;
}

__attribute__((noinline))
static int work(int xval, int yval) {
    int sum(0);
    for (int i=0; i<LOOP_BOUND; ++i) {
        int x(xval+sum);
        int y(yval+sum);
        int z = add(x, y);
        sum += z;
    }
    return sum;
}

int main(int , char* argv[]) {
    int result = work(*argv[1], *argv[2]);
    return result;
}

Si je compile avec -Os, il faut 0.38 s pour exécuter ce programme, et de 0,44 s si il est compilé avec -O2 ou -O3. Ces temps sont obtenus de manière cohérente et avec pratiquement pas de bruit (gcc 4.7.2, x86_64 GNU/Linux, Intel Core i5-3320M).

(Mise à jour: j'ai déplacé l'ensemble de l'assemblée des codes de github: Ils ont fait le post de ballonnement et, apparemment, ajouter très peu d'intérêt pour les questions que l' fno-align-* drapeaux ont le même effet.)

L'assembly généré avec -Os et -O2. Malheureusement, ma compréhension de l'assemblée est très limité, de sorte que je n'ai aucune idée si ce que j'ai fait suivant est correct: j'ai attrapé l'assemblée pour -O2 et de fusion de toutes ses différences dans l'assemblée pour -Os à l'exception de l' .p2align lignes, le résultat ici. Ce code s'exécute toujours dans 0.38 s et la seule différence est l' .p2align trucs.

Si je devine correctement, ce sont les rembourrages pour la pile de l'alignement. Selon Pourquoi ne GCC pad fonctions avec des Opr? c'est fait dans l'espoir que le code sera exécuté plus rapidement, mais, apparemment, cette optimisation est retournée dans mon cas.

Est-ce le rembourrage qui est le coupable dans cette affaire? Pourquoi et comment?

Le bruit qu'il fait assez bien fait de synchronisation micro-optimisations impossible.

Comment puis-je faire en sorte que cette accidentelle de la chance / malchance alignements ne sont pas interférer quand je fais des micro-optimisations (sans rapport avec la pile de l'alignement) sur C ou C++ codes sources?


Mise à JOUR:

Suite à Pascal Cuoq réponse j'ai bricolé un peu avec les alignements. En passant -O2 -fno-align-functions -fno-align-loops de gcc, tous .p2align sont partis de l'assemblée et l'exécutable généré s'exécute dans 0.38 s. Selon la doc de gcc:

-Os permet à tous -O2 optimisations [mais] -Os désactive la suite à l'optimisation des drapeaux:

  -falign-functions  -falign-jumps  -falign-loops 
  -falign-labels  -freorder-blocks  -freorder-blocks-and-partition 
  -fprefetch-loop-arrays

Donc, il semble comme une (mauvaise)problème d'alignement.

Je suis toujours sceptique sur -march=native comme le suggère Marat Dukhan de réponse. Je ne suis pas convaincu que ce n'est pas seulement interférer avec cette (mauvaise)problème d'alignement; il n'a absolument aucun effet sur ma machine. (Néanmoins, je upvoted sa réponse.)


Mise à JOUR 2:

Nous pouvons prendre en -Os hors de l'image. Les temps suivants sont obtenus par compilation avec

  • -O2 -fno-omit-frame-pointer 0.37 s

  • -O2 -fno-align-functions -fno-align-loops 0.37 s

  • -S -O2 puis de les déplacer manuellement l'assemblée de l' add() après work() 0.37 s

  • -O2 De 0,44 s

Il ressemble pour moi à la distance de l' add() à partir de l'appel site compte beaucoup. J'ai essayé perf , mais la sortie de l' perf stat et perf report fait très peu de sens pour moi. Cependant, je ne pouvait obtenir un résultat cohérent:

-O2:

 602,312,864 stalled-cycles-frontend   #    0.00% frontend cycles idle   
       3,318 cache-misses                                                
 0.432703993 seconds time elapsed
 [...]
 81.23%  a.out  a.out              [.] work(int, int)
 18.50%  a.out  a.out              [.] add(int const&, int const&) [clone .isra.0]
 [...]
       │   __attribute__((noinline))
       │   static int add(const int& x, const int& y) {
       │       return x + y;
100.00 │     lea    (%rdi,%rsi,1),%eax
       │   }
       │   ← retq
[...]
       │            int z = add(x, y);                                                                                                                                 
  1.93 │    → callq  add(int const&, int const&) [clone .isra.0]                                                                                                       
       │            sum += z;                                                                                                                                          
 79.79 │      add    %eax,%ebx   

Pour fno-align-*:

 604,072,552 stalled-cycles-frontend   #    0.00% frontend cycles idle   
       9,508 cache-misses                                                
 0.375681928 seconds time elapsed
 [...]
 82.58%  a.out  a.out              [.] work(int, int)
 16.83%  a.out  a.out              [.] add(int const&, int const&) [clone .isra.0]
 [...]
       │   __attribute__((noinline))
       │   static int add(const int& x, const int& y) {
       │       return x + y;
 51.59 │     lea    (%rdi,%rsi,1),%eax
       │   }
[...]
       │    __attribute__((noinline))                                                                                                                                  
       │    static int work(int xval, int yval) {                                                                                                                      
       │        int sum(0);                                                                                                                                            
       │        for (int i=0; i<LOOP_BOUND; ++i) {                                                                                                                     
       │            int x(xval+sum);                                                                                                                                   
  8.20 │      lea    0x0(%r13,%rbx,1),%edi                                                                                                                             
       │            int y(yval+sum);                                                                                                                                   
       │            int z = add(x, y);                                                                                                                                 
 35.34 │    → callq  add(int const&, int const&) [clone .isra.0]                                                                                                       
       │            sum += z;                                                                                                                                          
 39.48 │      add    %eax,%ebx                                                                                                                                         
       │    }   

Pour -fno-omit-frame-pointer:

 404,625,639 stalled-cycles-frontend   #    0.00% frontend cycles idle   
      10,514 cache-misses                                                
 0.375445137 seconds time elapsed
 [...]
 75.35%  a.out  a.out              [.] add(int const&, int const&) [clone .isra.0]                                                                                     ▒
 24.46%  a.out  a.out              [.] work(int, int)  
 [...]
       │   __attribute__((noinline))
       │   static int add(const int& x, const int& y) {
 18.67 │     push   %rbp
       │       return x + y;
 18.49 │     lea    (%rdi,%rsi,1),%eax
       │   const int LOOP_BOUND = 200000000;
       │
       │   __attribute__((noinline))
       │   static int add(const int& x, const int& y) {
       │     mov    %rsp,%rbp
       │       return x + y;
       │   }
 12.71 │     pop    %rbp
       │   ← retq
 [...]
       │            int z = add(x, y);                                                                                                                                 
       │    → callq  add(int const&, int const&) [clone .isra.0]                                                                                                       
       │            sum += z;                                                                                                                                          
 29.83 │      add    %eax,%ebx    

Il semble que nous caler sur l'appel à l' add() dans le scénario de croissance lente.

J'ai examiné tout ce qu' perf -e peut cracher sur ma machine, et non seulement les statistiques qui figurent ci-dessus.

Pour le même exécutable, l' stalled-cycles-frontend montre de corrélation linéaire avec le temps d'exécution; je n'ai pas remarqué quelque chose d'autre qui serait en corrélation clairement. (En comparant stalled-cycles-frontend pour les différents exécutables ne fait pas de sens pour moi.)

J'ai inclus la cache comme il est venu comme le premier commentaire. J'ai examiné tous les défauts de cache qui peuvent être mesurés sur ma machine par perf, pas seulement ceux donnés ci-dessus. Le cache sont très très bruyant et montrent peu ou pas de corrélation avec le temps de l'exécution.

572voto

Marat Dukhan Points 4768

Par défaut, les compilateurs d'optimiser la "moyenne" du processeur. Depuis les différents processeurs de faveur de différentes séquences d'instructions, les optimisations du compilateur activé par -O2 peut bénéficier de la moyenne du processeur, mais de diminuer les performances de votre processeur (et la même chose s'applique -Os). Si vous essayez de le même exemple sur des processeurs différents, vous trouverez que sur certains d'entre eux bénéficient d' -O2 alors que d'autres sont plus favorables à l' -Os optimisations.

Voici les résultats pour l' time ./test 0 0 sur plusieurs processeurs (du temps à l'utilisateur signalé):

Processor (System-on-Chip)             Compiler   Time (-O2)  Time (-Os)  Fastest
AMD Opteron 8350                       gcc-4.8.1    0.704s      0.896s      -O2
AMD FX-6300                            gcc-4.8.1    0.392s      0.340s      -Os
AMD E2-1800                            gcc-4.7.2    0.740s      0.832s      -O2
Intel Xeon E5405                       gcc-4.8.1    0.603s      0.804s      -O2
Intel Xeon E5-2603                     gcc-4.4.7    1.121s      1.122s       -
Intel Core i3-3217U                    gcc-4.6.4    0.709s      0.709s       -
Intel Core i3-3217U                    gcc-4.7.3    0.708s      0.822s      -O2
Intel Core i3-3217U                    gcc-4.8.1    0.708s      0.944s      -O2
Intel Core i7-4770K                    gcc-4.8.1    0.296s      0.288s      -Os
Intel Atom 330                         gcc-4.8.1    2.003s      2.007s      -O2
ARM 1176JZF-S (Broadcom BCM2835)       gcc-4.6.3    3.470s      3.480s      -O2
ARM Cortex-A8 (TI OMAP DM3730)         gcc-4.6.3    2.727s      2.727s       -
ARM Cortex-A9 (TI OMAP 4460)           gcc-4.6.3    1.648s      1.648s       -
ARM Cortex-A9 (Samsung Exynos 4412)    gcc-4.6.3    1.250s      1.250s       -
ARM Cortex-A15 (Samsung Exynos 5250)   gcc-4.7.2    0.700s      0.700s       -
Qualcomm Snapdragon APQ8060A           gcc-4.8       1.53s       1.52s      -Os

Dans certains cas, vous pouvez atténuer l'effet de désavantager les optimisations en demandant gcc pour optimiser votre processeur particulier (à l'aide des options -mtune=native ou -march=native):

Processor            Compiler   Time (-O2 -mtune=native) Time (-Os -mtune=native)
AMD FX-6300          gcc-4.8.1         0.340s                   0.340s
AMD E2-1800          gcc-4.7.2         0.740s                   0.832s
Intel Xeon E5405     gcc-4.8.1         0.603s                   0.803s
Intel Core i7-4770K  gcc-4.8.1         0.296s                   0.288s

Mise à jour: sur Ivy Bridge à base de Core i3 trois versions de l' gcc (4.6.4, 4.7.3, et 4.8.1) produire des binaires avec significativement différente de la performance, mais le code de l'assembly a que de subtiles variations. Jusqu'à présent, je n'ai pas d'explication à ce fait.

L'assemblage de l' gcc-4.6.4 -Os (s'exécute dans 0.709 secondes):

00000000004004d2 <_ZL3addRKiS0_.isra.0>:
  4004d2:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004d5:       c3                      ret

00000000004004d6 <_ZL4workii>:
  4004d6:       41 55                   push   r13
  4004d8:       41 89 fd                mov    r13d,edi
  4004db:       41 54                   push   r12
  4004dd:       41 89 f4                mov    r12d,esi
  4004e0:       55                      push   rbp
  4004e1:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  4004e6:       53                      push   rbx
  4004e7:       31 db                   xor    ebx,ebx
  4004e9:       41 8d 34 1c             lea    esi,[r12+rbx*1]
  4004ed:       41 8d 7c 1d 00          lea    edi,[r13+rbx*1+0x0]
  4004f2:       e8 db ff ff ff          call   4004d2 <_ZL3addRKiS0_.isra.0>
  4004f7:       01 c3                   add    ebx,eax
  4004f9:       ff cd                   dec    ebp
  4004fb:       75 ec                   jne    4004e9 <_ZL4workii+0x13>
  4004fd:       89 d8                   mov    eax,ebx
  4004ff:       5b                      pop    rbx
  400500:       5d                      pop    rbp
  400501:       41 5c                   pop    r12
  400503:       41 5d                   pop    r13
  400505:       c3                      ret

L'assemblage de l' gcc-4.7.3 -Os (s'exécute dans 0.822 secondes):

00000000004004fa <_ZL3addRKiS0_.isra.0>:
  4004fa:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004fd:       c3                      ret

00000000004004fe <_ZL4workii>:
  4004fe:       41 55                   push   r13
  400500:       41 89 f5                mov    r13d,esi
  400503:       41 54                   push   r12
  400505:       41 89 fc                mov    r12d,edi
  400508:       55                      push   rbp
  400509:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  40050e:       53                      push   rbx
  40050f:       31 db                   xor    ebx,ebx
  400511:       41 8d 74 1d 00          lea    esi,[r13+rbx*1+0x0]
  400516:       41 8d 3c 1c             lea    edi,[r12+rbx*1]
  40051a:       e8 db ff ff ff          call   4004fa <_ZL3addRKiS0_.isra.0>
  40051f:       01 c3                   add    ebx,eax
  400521:       ff cd                   dec    ebp
  400523:       75 ec                   jne    400511 <_ZL4workii+0x13>
  400525:       89 d8                   mov    eax,ebx
  400527:       5b                      pop    rbx
  400528:       5d                      pop    rbp
  400529:       41 5c                   pop    r12
  40052b:       41 5d                   pop    r13
  40052d:       c3                      ret

L'assemblage de l' gcc-4.8.1 -Os (s'exécute dans 0.994 secondes):

00000000004004fd <_ZL3addRKiS0_.isra.0>:
  4004fd:       8d 04 37                lea    eax,[rdi+rsi*1]
  400500:       c3                      ret

0000000000400501 <_ZL4workii>:
  400501:       41 55                   push   r13
  400503:       41 89 f5                mov    r13d,esi
  400506:       41 54                   push   r12
  400508:       41 89 fc                mov    r12d,edi
  40050b:       55                      push   rbp
  40050c:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  400511:       53                      push   rbx
  400512:       31 db                   xor    ebx,ebx
  400514:       41 8d 74 1d 00          lea    esi,[r13+rbx*1+0x0]
  400519:       41 8d 3c 1c             lea    edi,[r12+rbx*1]
  40051d:       e8 db ff ff ff          call   4004fd <_ZL3addRKiS0_.isra.0>
  400522:       01 c3                   add    ebx,eax
  400524:       ff cd                   dec    ebp
  400526:       75 ec                   jne    400514 <_ZL4workii+0x13>
  400528:       89 d8                   mov    eax,ebx
  40052a:       5b                      pop    rbx
  40052b:       5d                      pop    rbp
  40052c:       41 5c                   pop    r12
  40052e:       41 5d                   pop    r13
  400530:       c3                      ret

211voto

Ali Points 18740

Mon collègue m'a aidé à trouver une réponse plausible à ma question. Il a remarqué l'importance de l'256 frontière d'octet. Il n'est pas inscrit ici et m'a encouragé à publier la réponse moi-même (et de prendre toute la gloire).


Réponse courte:

Est-ce le rembourrage qui est le coupable dans cette affaire? Pourquoi et comment?

Tout se résume à l'alignement. Les alignements peuvent avoir un impact significatif sur la performance, c'est pourquoi nous avons l' -falign-* drapeaux en premier lieu.

J'ai soumis un (faux?) rapport de bug pour les développeurs de gcc. Il s'avère que le comportement par défaut est "nous aligner les boucles de 8 octets par défaut, mais essayez de les aligner sur 16 octets si nous n'avons pas besoin de remplir de plus de 10 octets." Apparemment, ce n'est pas le meilleur choix dans ce cas particulier et sur ma machine. Clang 3.4 (tronc) avec -O3 le l'alignement et le code généré ne montrent pas ce comportement étrange.

Bien sûr, si inappropriée d'un alignement est fait, ça rend les choses pires. Inutile / mauvais alignement juste mange octets pour aucune raison et augmente potentiellement le cache, etc.

Le bruit qu'il fait assez bien fait de synchronisation micro-optimisations impossible.

Comment puis-je faire en sorte que cette accidentelle de la chance / malchance alignements ne sont pas interférer quand je fais des micro-optimisations (sans rapport avec la pile l'alignement) sur C ou C++ codes sources?

Simplement en disant gcc pour faire l'alignement à droite:

g++ -O2 -falign-functions=16 -falign-loops=16


Réponse longue:

Le code va s'exécuter plus lentement si:

  • un XX limite d'octets coupures add() dans le milieu (XX être dépend de la machine).

  • si l'appel à l' add() a pour sauter par-dessus un XX limite d'octets et la cible n'est pas aligné.

  • si add() n'est pas aligné.

  • si la boucle n'est pas aligné.

Les 2 premières sont magnifiquement visible sur les codes et les résultats que Marat Dukhan gentiment posté. Dans ce cas, gcc-4.8.1 -Os (s'exécute dans 0.994 secondes):

00000000004004fd <_ZL3addRKiS0_.isra.0>:
  4004fd:       8d 04 37                lea    eax,[rdi+rsi*1]
  400500:       c3   

256 frontière d'octet coupures add() dans le milieu et ni add() ni la boucle est aligné. Surprise, Surprise, c'est le plus lent cas!

En cas gcc-4.7.3 -Os (s'exécute dans 0.822 secs), les 256 octets limite seulement des coupes dans une section froide (mais ni boucle, ni add() coupé):

00000000004004fa <_ZL3addRKiS0_.isra.0>:
  4004fa:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004fd:       c3                      ret

[...]

  40051a:       e8 db ff ff ff          call   4004fa <_ZL3addRKiS0_.isra.0>

Rien n'est aligné, et l'appel à l' add() a pour sauter par-dessus les 256 octets de la frontière. Ce code est le deuxième plus lent.

En cas gcc-4.6.4 -Os (s'exécute dans 0.709 secs), même si rien n'est aligné, l'appel à l' add() n'a pas à sauter par-dessus les 256 octets frontière et la cible est exactement 32 octets de suite:

  4004f2:       e8 db ff ff ff          call   4004d2 <_ZL3addRKiS0_.isra.0>
  4004f7:       01 c3                   add    ebx,eax
  4004f9:       ff cd                   dec    ebp
  4004fb:       75 ec                   jne    4004e9 <_ZL4workii+0x13>

C'est le plus rapide des trois. Pourquoi les 256 octets limite est speacial sur sa machine, je vais le laisser jusqu'à lui pour le comprendre. Je n'ai pas un tel processeur.

Maintenant, sur ma machine je n'ai pas ce 256 octets effet de frontière. Seule la fonction et la boucle de l'alignement des coups de pied sur ma machine. Si je passe g++ -O2 -falign-functions=16 -falign-loops=16 puis tout revient à la normale: j'ai toujours le cas le plus rapide et le temps n'est pas sensible à l' -fno-omit-frame-pointer drapeau plus. Je peux transmettre g++ -O2 -falign-functions=32 -falign-loops=32 ou tout multiple de 16, le code n'est pas sensible à l'un ou l'autre.

J'ai d'abord remarqué en 2009 que gcc (au moins sur mes projets et sur mon les machines) ont tendance à générer beaucoup plus rapidement code si j' optimiser pour la taille (-Os) au lieu de la vitesse (-O2 ou -O3) et j'ai été vous demandez-vous jamais pourquoi depuis.

Une explication probable est que j'ai eu des points chauds qui ont été sensibles à l'alignement, comme dans cet exemple. Par jouer avec les drapeaux (en passant -Os au lieu de -O2), ces points chauds sont alignées dans un chanceux moyen par accident et le code est devenu plus rapide. Il n'avait rien à voir avec l'optimisation de la taille: c'était par pur accident que les hotspots ai aligné mieux. À partir de maintenant, je vais vérifier les effets de l'alignement sur mes projets.

Oh, et une chose de plus. Comment de tels points de connexion surviennent, comme celui montré dans l'exemple? Comment peut-inline de ces petites fonctions comme l' add() échouent?

Réfléchissez à ceci:

// add.cpp
int add(const int& x, const int& y) {
    return x + y;
}

et dans un fichier séparé:

// main.cpp
int add(const int& x, const int& y);

const int LOOP_BOUND = 200000000;

__attribute__((noinline))
static int work(int xval, int yval) {
    int sum(0);
    for (int i=0; i<LOOP_BOUND; ++i) {
        int x(xval+sum);
        int y(yval+sum);
        int z = add(x, y);
        sum += z;
    }
    return sum;
}

int main(int , char* argv[]) {
    int result = work(*argv[1], *argv[2]);
    return result;
}

et compilé en tant que: g++ -O2 add.cpp main.cpp.

      gcc ne inline add()!

C'est tout, c'est facile à unintendedly créer des points d'accès comme celui de l'OP. Bien sûr, c'est en partie de ma faute: gcc est un excellent compilateur. Si la compilation ci-dessus: g++ -O2 -flto add.cpp main.cpp, qui est, si j'effectue le lien à l'optimisation du temps, le code s'exécute dans 0.19 s!

(Inlining est artificiellement désactivé dans l'OP, donc, le code de l'OP était 2x plus lent).

80voto

Gene Points 20184

Je suis en ajoutant ce post-accepter de souligner que les effets de l'alignement sur la performance globale de programmes - y compris les grands - a été étudié. Par exemple, cet article (et je crois qu'une version de ce est également apparu dans CACM) montre comment l'ordre des liens et de l'environnement de système d'exploitation les changements de taille seule était suffisante pour déplacer les performances de façon significative. Ils attribuent cela à l'alignement des "boucles".

Ce document, intitulé "la Production de données erronées sans rien faire évidemment faux!" dit que, par inadvertance les biais expérimentaux en raison de presque incontrôlable des différences dans l'exécution du programme, les environnements probablement rend de nombreux résultats d'un benchmark de sens.

Je pense que vous êtes de la rencontre d'un angle différent sur le même constat.

Pour le code critique, c'est un très bon argument pour les systèmes qui permettent d'évaluer l'environnement lors de l'installation ou de l'exécution et de choisir le local meilleur d'entre différemment des versions optimisées de routines de base.

36voto

Pascal Cuoq Points 39606

Je pense que vous pouvez obtenir le même résultat que ce que vous avez fait:

J'ai saisi l'assemblée pour -O2 et de fusion de toutes ses différences dans l'assemblée pour -Os à l'exception de l' .p2align lignes:

... en utilisant -O2 -falign-functions=1 -falign-jumps=1 -falign-loops=1 -falign-labels=1. J'ai été la compilation de tout ce avec ces options, qui ont été plus vite que la plaine -O2 chaque fois que j'ai pris la peine de mesurer, pour 15 ans.

Aussi, pour un tout autre contexte (y compris un compilateur différent), j'ai remarqué que la situation est similaire: l'option qui est censé "optimiser la taille du code, plutôt que de la vitesse" optimise pour le code de la taille et de la vitesse.

Si je devine correctement, ce sont les rembourrages pour la pile de l'alignement.

Non, cela n'a rien à voir avec la pile, l'Opr, qui sont générés par défaut et que les options -falign-*=1 prévenir les sont pour de code de l'alignement.

Selon Pourquoi ne GCC pad fonctions avec des Opr? c'est fait dans l'espoir que le code sera exécuté plus rapidement, mais, apparemment, cette optimisation est retournée dans mon cas.

Est-ce le rembourrage qui est le coupable dans cette affaire? Pourquoi et comment?

Il est très probable que le rembourrage est le coupable. La raison rembourrage est jugé nécessaire et utile dans certains cas, c'est que le code est généralement extraites dans les lignes de 16 octets (voir Agner le Brouillard de l'optimisation des ressources pour les détails, qui peuvent varier selon le modèle de processeur). L'alignement d'une fonction, boucle, ou une étiquette sur 16 octets limite signifie que les chances sont statistiquement accru que l'on réduire le nombre de lignes sera nécessaire pour contenir la fonction ou à une boucle. Évidemment, ça se retourne parce que ces Opr, de réduire la densité de code et donc l'efficacité de la mémoire cache. Dans le cas de boucles et de l'étiquette, l'Opr, peut-être même besoin d'être exécutée une seule fois (lors de l'exécution arrive à la boucle/label normalement, plutôt qu'à un saut).

8voto

Daniel Frey Points 30752

Je ne suis pas un expert dans ce domaine, mais je crois me souvenir que les processeurs modernes sont très sensibles quand il s'agit de la branche de prédiction. Les algorithmes utilisés pour prédire les branches sont (ou, au moins, étaient de retour dans les jours, j'ai écrit du code assembleur) selon plusieurs propriétés du code, y compris la distance de la cible et de la direction.

Le scénario qui vient à l'esprit est de petites boucles. Lorsque la branche a été d'aller en arrière et la distance n'est pas trop loin, la direction générale predicition a été optimiser pour ce cas, comme toutes les petites boucles sont faites de cette façon. Les mêmes règles sont susceptibles d'entrer en jeu lorsque vous changez l'emplacement de add et work dans le code généré ou lorsque la position de change légèrement.

Cela dit, je n'ai aucune idée de comment la vérifier et je voulais juste vous faire savoir que cela peut être quelque chose que vous voulez examiner.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X