while (condition) {
...
}
Рабочий процесс:
- проверить состояние;
- если false, перейти за пределы цикла;
- выполнить одну итерацию;
- перейти наверх.
if (condition) do {
...
} while (condition);
Рабочий процесс:
- проверить состояние;
- если false, перейти за пределы цикла;
- выполнить одну итерацию;
- проверить состояние;
- если это так, переходите к шагу 3.
Сравнивая эти два, вы можете легко увидеть, что последний может вообще не делать никаких прыжков при условии, что цикл проходит ровно один шаг, и, как правило, количество прыжков будет на один меньше, чем количество итераций. Первому придется вернуться назад, чтобы проверить условие, только чтобы выйти из цикла, когда условие ложно.
Переходы в современных конвейерных архитектурах ЦП могут быть довольно дорогими: поскольку ЦП завершает выполнение проверок перед переходом, инструкции после этого перехода уже находятся в середине конвейера. Вся эта обработка должна быть отброшена, если предсказание ветвления не удается. Дальнейшее выполнение откладывается на время перезапуска конвейера.
Объяснение упомянутого предсказания перехода : для каждого вида условного перехода ЦП имеет две инструкции, каждая из которых включает ставку на результат. Например, вы можете поместить инструкцию « прыгать, если не ноль, ставка не ноль » в конце цикла, потому что прыжок должен быть выполнен на всех итерациях, кроме последней. Таким образом, ЦП начинает прокачивать свой конвейер инструкциями, следующими за целью перехода, а не инструкциями, следующими за самой инструкцией перехода.
Важная заметка
Пожалуйста, не воспринимайте это как пример оптимизации на уровне исходного кода. Это было бы совершенно ошибочным, поскольку, как уже ясно из вашего вопроса, преобразование из первой формы во вторую - это то, что JIT-компилятор делает в обычном порядке, полностью самостоятельно.