Lequel est plus vite? ++, + = ou x + 1?

J’utilise C # (cette question est également valable pour des langages similaires comme C ++) et j’essaie de trouver le moyen le plus rapide et le plus efficace d’incrémenter. Ce n’est pas juste un ou deux incréments, dans mon jeu, c’est comme 300 incréments par seconde. Comme les images de chaque image-object sur l’écran sont incrémentées, la vitesse et les positions de mon personnage, le décalage de la caméra, etc. Je pense donc de quelle manière est la plus efficace? Par exemple, pour augmenter de 5 y_pos sur chaque mouvement que je peux faire:

1.

 Player.YPos += 5; 

2

 Player.YPos = Player.YPos + 5; 

3

 for (int i = 0; i < 5; i++) { Player.YPos++; } 

Quel est le plus efficace (et le plus rapide)?

(La réponse spécifique à C # étant donné que C ++ peut varier considérablement.)

1 et 2 sont équivalents.

3 serait certainement plus lent.

Cela dit, si vous faites cela 300 fois par seconde, vous ne remarquerez aucune différence. Êtes-vous conscient de tout ce qu’un ordinateur peut faire en termes de CPU + mémoire brute en une seconde? En général, vous devez écrire du code pour plus de clarté, ce qui est le plus important. Ne vous inquiétez pas pour la performance, mais seulement si vous avez un moyen de la mesurer, afin de a) dire si vous avez besoin de vous inquiéter et b) si des modifications ont réellement amélioré la performance.

Dans ce cas, je dirais que l’option 1 est la plus claire, c’est ce que j’utiliserais.

Les options 1 et 2 donneront lieu à un code identique produit par le compilateur. L’option 3 sera beaucoup plus lente.

C’est une erreur que i++ soit plus rapide que i += 1 ou même i = i + 1 . Tous les compilateurs décents transformeront ces trois instructions dans le même code.

Pour une opération aussi sortingviale que l’addition, écrivez le code le plus clair et laissez le compilateur se soucier de le rendre rapide.

Le compilateur doit produire le même assemblage pour 1 et 2 et peut dérouler la boucle dans l’option 3. Face à des questions comme celle-ci, un outil utile que vous pouvez utiliser pour tester de manière empirique ce qui se passe est d’examiner l’assemblage produit par le compilateur. . En g ++, cela peut être réalisé en utilisant le commutateur -S .

Par exemple, les options 1 et 2 produisent cet assembleur lorsqu’elles ont été compilées avec la commande g++ -S inc.cpp (à l’aide de g ++ 4.5.2)

 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 addl $5, -4(%rbp) movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc 

g ++ produit un assembleur nettement moins efficace pour l’option 3:

 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 movl $0, -8(%rbp) jmp .L2 .L3: addl $1, -4(%rbp) addl $1, -8(%rbp) .L2: cmpl $4, -8(%rbp) setle %al testb %al, %al jne .L3 movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc 

Mais avec l’optimisation sur (même -O1), g ++ produit ceci pour les 3 options:

 main: .LFB0: .cfi_startproc leal 5(%rdi), %eax ret .cfi_endproc 

g ++ non seulement déroule la boucle dans l’option 3, mais utilise également l’ instruction lea pour effectuer l’addition en une seule instruction au lieu de se déplacer avec mov .

Donc, g ++ produira toujours le même assemblage pour les options 1 et 2. g ++ ne produira le même assemblage que pour les 3 options uniquement si vous activez explicitement l’optimisation (ce qui est probablement le comportement attendu).

(et il semble que vous devriez également pouvoir inspecter l’assemblage produit par C # , bien que je ne l’aie jamais essayé)

Ils sont les mêmes:

 static void Main(ssortingng[] args) { int a = 0; a++; a +=1; a = a+1; } 

Le code ci-dessus dans ILSpy est:

 private static void Main(ssortingng[] args) { int a = 0; a++; a++; a++; } 

En outre, le IL pour tous ceux-ci est identique (en mode Release):

 .method private hidebysig static void Main(ssortingng[] args) cil managed { .entrypoint // Code size 15 (0xf) .maxstack 2 .locals init ([0] int32 a) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.1 IL_0004: add IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.1 IL_0008: add IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: add IL_000d: stloc.0 IL_000e: ret } // end of method Program::Main 

Les options 1 et 2 donneront un code identique après avoir été compilé. L’option 3 sera beaucoup plus lente car elle génère plus de code pour la boucle for impliquée.