while (condition) {
...
}
Flujo de trabajo:
- comprobar la condición;
- si es falso, salta al exterior del bucle;
- ejecutar una iteración;
- Saltar a la cima.
if (condition) do {
...
} while (condition);
Flujo de trabajo:
- comprobar la condición;
- si es falso, salte más allá del bucle;
- ejecutar una iteración;
- comprobar la condición;
- si es cierto, vaya al paso 3.
Comparando estos dos, puede ver fácilmente que el último puede no hacer ningún salto en absoluto, siempre que haya exactamente un paso a través del bucle, y generalmente el número de saltos será uno menos que el número de iteraciones. El primero tendrá que saltar hacia atrás para verificar la condición, solo para saltar fuera del ciclo cuando la condición sea falsa.
Los saltos en arquitecturas modernas de CPU con canalización pueden ser bastante costosos: como la CPU está terminando la ejecución de las comprobaciones antes del salto, las instrucciones posteriores a ese salto ya están en el medio del proceso. Todo este procesamiento debe descartarse si falla la predicción de la rama. La ejecución adicional se retrasa mientras se vuelve a cebar la tubería.
Explicando la predicción de bifurcación mencionada : para cada tipo de salto condicional, la CPU tiene dos instrucciones, cada una de las cuales incluye una apuesta sobre el resultado. Por ejemplo, colocaría una instrucción que diga " salte si no es cero, apostando a que no sea cero " al final de un ciclo porque el salto tendrá que realizarse en todas las iteraciones excepto en la última. De esa manera, la CPU comienza a bombear su canalización con las instrucciones que siguen al objetivo de salto en lugar de las que siguen la instrucción de salto en sí.
Nota IMPORTANTE
Por favor, no tome esto como un ejemplo de cómo optimizar a nivel de código fuente. Eso sería completamente equivocado ya que, como ya se desprende de su pregunta, la transformación del primer formulario al segundo es algo que el compilador JIT hace como una cuestión de rutina, completamente por sí solo.