Вот и думаю, как уложить это с тем, что Цитата:
код, генерируемый современными компиляторами практически невозможно сделать оптимальнее в рукопашную
- даже на таком банальном примере...
Никаких расхождений теории и практики. Проблема немного более тонка. Это в рассматриваемом примере регистров валом. А обычно они все заняты - кодом, который выполняется. Чем интенсивнее код использует регистры - тем он оптимальнее, и по быстродействию, и по размеру.
Кроме того, в принципе, для AVR есть возможность указать компилятору оторвать несколько регистров для рукопашного использования. Иногда, в крайних случаях, этим можно пользоваться - например, для оптимизации процедур прерывания, когда каждый такт на счету и, например, убирание пролога и эпилога функции прерывания (которые заняты сохранением/восстановлением регистров в/из стека) дает приличный выигрыш по производительности.
Однако, тут стоит смотреть на проблему в комплексе. Если вычисления в обычных функциях достаточно тяжелы и отнятие регистров приводит к тому, что в функциях все локальные переменные в регистры не помещаются - обычно происходит резкое падение производительности основного кода.
Так что надо выбирать - "или шашечки, или ехать".
А Функция "Обождать Х ms" имхо используется чаще, чем какая-либо другая!
Это она в таком очень простом примере используется так вот в наглую. В довольно обширных системах обычно таких процедур ожидания нет - ибо в это время проц просто простаивает. А обычно надо еще что-то делать. Но тут на помощь приходят всякие операционные системы реального времени с их потоками, сообщениями и прочим. Не обязательно общедоступные (например, FreeRTOS), часто и просто некие самописные вариации на тему RTOS.