Страницы

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

понедельник, 1 октября 2018 г.

Самый быстрый целочисленный тип

Почему целочисленный тип, меньший чем размер машинного слова, обрабатывается медленней чем тип размерностью = размеру машинного слова?
Тогда какой тип быстрей будет обрабатываться int > T или int < T?


Ответ

Хотел я поработать разрушителем мифов ... Не получилось. "Миф" выдержал...
Была сделана тестовая программа на C:
int main() { unsigned char b,c,d; unsigned int a; struct timespec t,t1; int i; clock_gettime(CLOCK_REALTIME, &t); b=5; c=7; d=45; for(i=0;i<50000000;i++) { for(a=0;a<200;a++) { b^=c+d; c=d-b; d=b^5; } } clock_gettime(CLOCK_REALTIME, &t1); printf("Difference %ld %ld
",t1.tv_sec-t.tv_sec,t1.tv_nsec-t.tv_nsec); printf("%d %d %d %d
",a,b,c,d); }
Назовем ее программой 'A'. И точно такая же программа, но с переменными unsigned int b,c,d - программа 'B'. При компиляции gcc с оптимизацией -O3 получился следующий ассемблерный код основных циклов, с моими комментариями с смыслом операций:
Программа А Программа B .L2: .L2: movl $200, %eax a=200 movl $200, %eax a=200 .L3: .L3: addl %r9d, %r8d c+=d addl %r9d, %r8d c+=d movl %r9d, %ecx X=d xorl %r8d, %ebx b^=c movl $5, %r9d d=5 movl %r9d, %r8d c=d xorl %r8d, %ebx b^=c movl %ebx, %r9d d=b subb %bl, %cl X-=b subl %ebx, %r8d c-=b xorl %ebx, %r9d d^=b xorl $5, %r9d d^=5 subl $1, %eax a-- subl $1, %eax a-- movl %ecx, %r8d c=X jne .L3 for(a) jne .L3 for(a) subl $1, %edx i-- subl $1, %edx i-- jne .L2 for(i) jne .L2 for(i)
Результаты выполнения: Difference 22 -30098974 Difference 19 -697228347 Difference 22 394100751 Difference 18 2860932 Difference 22 -37226465 Difference 18 3254312 Difference 22 67398660 Difference 18 43898871 Difference 22 -29109230 Difference 18 449544279
Первое, что бросается в глаза, в варианте 'A' оптимизатор вводит новую "переменную" 'X', а точнее выполняет пересылку d в регистр ecx работает с ним и потом возвращает обратно в d. Это связано с тем, что под переменную d он использует регистр R9 с младшим байтом которого независимо работать невозможно, а для вычитания он предпочитает использовать операцию той размерности, которую мы попросили. Поэтому использует ECX, младший байт которого доступен как CL
Фактически оптимизатор как с 1 байтом в данном случае выполняет только вычитание. А сложение и XOR он спокойно выполняет в полноразмерных, 4х байтовых регистрах, не опасаясь побочных эффектов.
Дабы быстрее развеять миф я решил заменить в варианте 'A' subb bl,cl, на равнозначную 4х байтовую subl ebx,ecx. И тут меня ждал сюрприз, без каких либо еще правок программа стала выполнятся 18 секунд, вместо 22. Процессор (в моем случае Core i7) выполняет вычитание в 1 байтовых регистрах медленнее, чем в "полных", 4х байтовых. После я попробовал так же операции add и xor и получил те же результаты.
После этого я сделал программу 'C' с типами unsigned long long, компилятор сгенерил нормальные 64х битные операции, которые показали те же 18 секунд. Т.е. в 64х битном режиме операции с int и long работают одинаково быстро. Проверка с типом short int, т.е. c 16 битным дала 22 секунды, как и с 8-битным.
Итого: Современные процессоры Intel, как минимум Core i7, выполняет операции в однобайтовых регистрах медленнее, чем в 4х байтовых, почему - спросите архитекторов процессоров Intel. Кроме того, в архитектуре x86 только с 4 из 16 регистров общего назначения можно работать как с байтовыми, следовательно оптимизатору приходится генерить более сложный код для обеспечения работы с такими типами.

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

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