while (condition) {
...
}
Flux de travail:
- vérifier l'état;
- si faux, sauter à l'extérieur de la boucle;
- exécuter une itération;
- sauter en haut.
if (condition) do {
...
} while (condition);
Flux de travail:
- vérifier l'état;
- si faux, passez au-delà de la boucle;
- exécuter une itération;
- vérifier l'état;
- si vrai, passez à l'étape 3.
En comparant ces deux, vous pouvez facilement voir que ce dernier peut ne pas faire de sauts du tout, à condition qu'il y ait exactement un pas dans la boucle, et généralement le nombre de sauts sera inférieur d'un au nombre d'itérations. Le premier devra revenir en arrière pour vérifier la condition, seulement pour sauter hors de la boucle lorsque la condition est fausse.
Les sauts sur des architectures CPU modernes en pipeline peuvent être assez coûteux: comme le CPU termine l'exécution des vérifications avant le saut, les instructions au-delà de ce saut sont déjà au milieu du pipeline. Tout ce traitement doit être ignoré si la prédiction de branchement échoue. La poursuite de l'exécution est retardée pendant le réamorçage du pipeline.
Explication de la prédiction de branche mentionnée : pour chaque type de saut conditionnel, le CPU a deux instructions, chacune comprenant un pari sur le résultat. Par exemple, vous mettriez une instruction disant " sauter sinon zéro, parier sur pas zéro " à la fin d'une boucle car le saut devra être fait sur toutes les itérations sauf la dernière. De cette façon, le CPU commence à pomper son pipeline avec les instructions suivant la cible de saut au lieu de celles suivant l'instruction de saut elle-même.
Note importante
Veuillez ne pas prendre cela comme un exemple d'optimisation au niveau du code source. Ce serait totalement erroné car, comme cela ressort de votre question, la transformation de la première forme en la seconde est quelque chose que le compilateur JIT fait de façon routinière, complètement de lui-même.