Страницы

Поиск по вопросам

воскресенье, 31 марта 2019 г.

Использование регистровых переменных сравнения в циклах Си

Все источники утверждают, что в циклах использование регистровых переменных очень хорошо для производительности. Для меня остаётся не до конца понятен вопрос как компилятор оптимизирует код в том месте, где происходит сравнение индексной переменной i с переменой length.
double array[100ULL];
size_t length = 20ULL;
for(register size_t i = 0ULL; i < length - 1ULL; i++){ array[i] = (double)i; }
В этом примере индексная переменная i - регистровая. Но сравнение внутри for происходит с переменной length, которая НЕ объявлена как регистровая. Помимо прочего перед сравнением происходит математическая операция.
Хочу для себя прояснить пару-тройку моментов:
Будет ли откомпиллированная программа при каждой итерации цикла производить эту математическую операцию (вычитание единицы из меременной length) раз за разом или эта операция будет произведена только один раз перед началом цикла? Догадается ли компилятор оптимизировать переменную length в регстровую? Какой код будет правильнее с точки зрения производительности и однозначности для компилятора: тот, что выше, или нижеприведённый? И есть ли вообще разница для современных компиляторов?
Второй вариант кода, в котором обе сравниваемые переменные регистровые и операция вычитания однозначно производится перед циклом:
double array[100ULL];
register const size_t length = 10ULL - 1ULL;
for(register size_t i = 0ULL; i < length; i++){ array[i] = (double)i; }
Трансляция кода в asm ситуацию не прояснила, поэтому задаю напрямую.


Ответ

Забудьте об этом слове register, оно давно не имеет никакого смысла.
Ответы на вопросы 1-2 зависят от компилятора; наверное, можно найти старый тупой компилятор, который не сумеет выполнить такие простые оптимизации.. А разумный - вообще может использовать какие-нибудь векторные команды процессора или что еще.
Так что ответ на вопрос 3 - нет, такой простой цикл будет оптимизирован любым более-менее разумным компилятором.
Кстати, VC++2017 просто развернул этот цикл в
mov eax, 1 xor ebx, ebx cvtsi2sd xmm1, rbx movsd QWORD PTR array$[rsp], xmm1 xorps xmm1, xmm1 cvtsi2sd xmm1, rax mov eax, 2 cvtsi2sd xmm2, rax mov eax, 3
.....
movsd QWORD PTR array$[rsp+128], xmm2 movsd QWORD PTR array$[rsp+136], xmm1 xorps xmm1, xmm1 cvtsi2sd xmm1, rax movsd QWORD PTR array$[rsp+144], xmm1
ругнувшись при этом на слово register как давно не поддерживаемое... Результат видите сами.
Очень старенький OpenWatcom пошел по циклическому пути -
L$1: cmp eax,13H jae L$2 mov dword ptr 320H[esp],eax xor ecx,ecx add edx,8 mov dword ptr 324H[esp],ecx inc eax fild qword ptr 320H[esp] fstp qword ptr -8[esp+edx] jmp L$1
но, как видите, вычислив 19 сразу, и работая только с регистрами...

Комментариев нет:

Отправить комментарий