Почему большее количество инструкций по сборке Pentium занимает меньше времени?

StackOverflow https://stackoverflow.com/questions/1099225

  •  11-09-2019
  •  | 
  •  

Вопрос

Ниже приведен отрывок из листинга двух последовательностей сборки Pentium.У нас есть внешний цикл, который пытается синхронизировать наши последовательности и выполняет таблицу вызовов, чтобы добраться до этих подпрограмм.Таким образом, внешний вызов каждый раз осуществляется из одного и того же места.Эти две последовательности отличаются тем, что первая содержит на одну инструкцию меньше, чем вторая.

Результаты, которые мы получаем на двух машинах Intel, сильно различаются.

Инструкция CPUID сообщает семейство, модель и степпинг.

Машина 1:Семья 6, Модель 15, Шаг 11.CPUZ сообщает «Intel Core 2 Duo E6750»
Инструкции выполняются со статистически одинаковой скоростью.

Машина 2:Семья 15, Модель 3, Шаг 3.CPUZ сообщает «Intel Pentium 4»
Первая последовательность занимает примерно на 8% больше времени, чем вторая.

Мы просто не можем объяснить увеличение времени.Не должно быть каких-либо различных удержаний флагов, прогнозирования ветвей, проблем с использованием регистров и т. д.По крайней мере, мы не можем этого сказать.

Есть ли у кого-нибудь идеи, почему выполнение первой последовательности на одной машине займет больше времени?

Редактировать:Добавление «XOR PTR ereg, 0» к первой последовательности приводит к тому, что время соответствует второму на Pentium 4.Любопытный.

Первая последовательность:

00000040               ALUSHIFT_AND_C_V_E LABEL NEAR
00000040  0F B7 04 55       MOVZX   EAX, gwr[(SIZEOF WORD) * EDX]       ; EAX = 0000000000000000 LLLLLLLLLLLLLLLL
   00000000 E
00000048  0F B7 14 4D       MOVZX   EDX, gwr[(SIZEOF WORD) * ECX]       ; EDX = 0000000000000000 RRRRRRRRRRRRRRRR
   00000000 E
00000050  23 C2             AND     EAX, EDX                            ; AX = L&R      (result)
00000052  A3 00000000 E     MOV     dvalue, EAX                         ; Save the temporary ALU/Shifter result
00000057  C3                RET                                         ; Return

Вторая последовательность:

00000060               ALUSHIFT_AND_C_V_NE LABEL NEAR
00000060  0F B7 04 55       MOVZX   EAX, gwr[(SIZEOF WORD) * EDX]       ; EAX = 0000000000000000 LLLLLLLLLLLLLLLL
   00000000 E
00000068  0F B7 14 4D       MOVZX   EDX, gwr[(SIZEOF WORD) * ECX]       ; EDX = 0000000000000000 RRRRRRRRRRRRRRRR
   00000000 E
00000070  23 C2             AND     EAX, EDX                            ; AX = L&R      (result)
00000072  80 35 00000000 E  XOR     BYTE PTR ereg, 1                    ; E = ~E
   01
00000079  A3 00000000 E     MOV     dvalue, EAX                         ; Save the temporary ALU/Shifter result
0000007E  C3                RET                                         ; Return
Это было полезно?

Решение

После Pentium I или II большинство оптимизаций, выполняемых компилятором, не проводились. как необходимый.Чип разложит эти инструкции на микрооперации, а затем оптимизирует их для вас.Это может быть разница в предсказании ветвления между чипами или тот факт, что XOR + RET так же дорог, как и простой RET.Я не настолько хорошо знаком с тем, какие модели Pentiums вы рассматриваете выше, чтобы сказать.Другая возможность заключается в том, что это также может быть проблема с строкой кэша или аппаратной разницей.

Может быть что-то есть в документации Intel, а может и нет.

Несмотря ни на что.Опытные программисты на ассемблере знают, что истина достигается посредством тестирования, которым вы и занимаетесь.

Другие советы

Оказывается, существует какое-то любопытное взаимодействие с расположением кода, вызывающее увеличение.Несмотря на то, что все выровнено по кэшу, переключение блоков кода привело к увеличению времени на Pentium-4.

Спасибо всем, кто нашел время, чтобы изучить это или посмотреть.

Вы можете добавить один, два и т. д. nops перед этим кодом (и больше ничего не менять), чтобы переместить его в кеш и посмотреть, есть ли эффекты кеширования (или просто отключить кеш).Предупреждение: всего лишь дополнительный no может изменить инструкцию в другом месте, которая больше не может достичь чего-либо, используя адресацию относительно компьютера, что может привести к увеличению количества байтов инструкции, что приведет как к перемещению тестируемого кода больше, чем хотелось бы, так и, возможно, к цепной реакции другие относительно адресные инструкции по изменению.

Даже если вы играете в кэш-игру, природа зверя здесь — это магия внутри чипа, которая принимает один поток инструкций и распределяет его между исполнительными блоками.

Настройка и тестирование — это то, что в конечном итоге действительно повышает производительность, даже если вы не понимаете, почему.Хотя, как только вы перенесете этот код на более старый или более новый чип, на другую материнскую плату или на то же семейство чипов, но с другим шагом, все ваши настройки производительности могут обернуться против вас.

Несколько месяцев назад со мной произошло нечто подобное.В моем проекте есть переключатель конфигурации, позволяющий использовать __thread для локальных переменных потока.Без него он будет использовать pthread_getspecific и тому подобное.Последний делает то же самое, что и __thread версия плюс вызов функции плюс некоторые дополнительные инструкции по настройке аргументов, сохранению регистров и т. д.Интересно, что более трудоемкая версия всегда работала быстрее.Правда, только на Pentium 4.Все остальные чипы вели себя вменяемо.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top