Si vous voulez comprendre ce que le compilateur fait, vous aurez juste besoin de tirer vers le haut un peu de montage. Je recommande ce site (j'ai déjà entrés dans le code de la question)): https://godbolt.org/g/FwZZOb.
Le premier exemple est plus intéressant.
int div(unsigned int num, unsigned int num2) {
if( num >= num2 ) return num % num2;
return num;
}
int div2(unsigned int num, unsigned int num2) {
return num % num2;
}
Génère:
div(unsigned int, unsigned int): # @div(unsigned int, unsigned int)
mov eax, edi
cmp eax, esi
jb .LBB0_2
xor edx, edx
div esi
mov eax, edx
.LBB0_2:
ret
div2(unsigned int, unsigned int): # @div2(unsigned int, unsigned int)
xor edx, edx
mov eax, edi
div esi
mov eax, edx
ret
Fondamentalement, le compilateur ne pas optimiser loin la branche, pour des cas très particuliers et les raisons logiques. Si la division entière était environ le même coût que la comparaison, puis la branche serait assez inutile. Mais la division entière (dont le module est réalisée avec le plus souvent) est en fait très cher: http://www.agner.org/optimize/instruction_tables.pdf. Les chiffres varient grandement d'une architecture et d'entier de taille, mais généralement, il pourrait être un temps de latence de n'importe où de 15 à près de 100 cycles.
En prenant une branche avant d'effectuer le module, vous pouvez vous épargner beaucoup de travail. Avis: le compilateur également de ne pas transformer le code sans une branche en branche au niveau de l'assemblage. C'est parce que la direction a un inconvénient: si le module finit par être nécessaire de toute façon, vous avez juste perdu un peu de temps.
Il n'y a pas moyen de prendre une décision raisonnable, la bonne optimisation sans connaître la fréquence relative avec laquelle idx < idx_max
sera vrai. Si les compilateurs (gcc et clang faire la même chose) optez pour la carte la code dans un relativement transparent, laissant ce choix dans les mains du développeur.
De sorte que la direction aurait pu être un très bon choix.
La deuxième branche devrait être complètement inutile, parce que la comparaison et l'attribution sont d'un coût comparable. Cela dit, vous pouvez le voir dans le lien que les compilateurs ne sera toujours pas effectuer cette optimisation s'ils ont une référence à la variable. Si la valeur est une variable locale (comme dans votre démontré code), alors le compilateur d'optimiser la branche à l'écart.
En somme, le premier morceau de code est peut-être une optimisation raisonnable, la seconde, sans doute fatigué programmeur.