Что "нового" в "новом" процессоре, если смотреть с точки зрения программиста
Недавно я был заинтересован в понимании низкоуровневых вычислений. Я понимаю, что современные широко используемые компьютеры следуют архитектуре x86/x86-64.
Насколько я понимаю, архитектура, точнее говоря, Instruction Set Architecture (ISA) - это набор инструкций, которые программист может выдавать ЦПУ.
Первый вопрос: ISA продолжает развиваться или остается прежним?
Я думаю, что он продолжает развиваться (то есть новые инструкции продолжают добавляться / изменяться предыдущие инструкции?), Но тогда как старый процессор сможет выполнять код, написанный с новыми инструкциями? (он не знает о новых инструкциях, но должен иметь возможность выполнять код, потому что он имеет архитектуру x86). Компилятор обрабатывает эту вещь или процессор? В основном, как один и тот же набор инструкций может работать на всех процессорах, старых или новых?
Наконец, кроме микроархитектуры, которая не касается программиста (поправьте меня, если я ошибаюсь), какие изменения видит программист при работе с новым процессором? Из-за изменений в микроархитектуре старые инструкции могут выполняться быстро из-за эффективной реализации. Но введены ли новые инструкции, чтобы разрешить то, что нельзя было сделать ранее? или что можно было сделать ранее с кучей инструкций, но теперь можно сделать с одной из-за изменений в оборудовании? Новые регистры? что-нибудь еще?
Делается ли это что-то вроде - если процессор поддерживает эту новую мощную инструкцию для более быстрого выполнения, тогда используйте новую инструкцию, в противном случае используйте более медленную более старую инструкцию. Если да, кто реализует это условие if - else? Компилятор? Если нет, то что происходит?
1 ответ
Как и большинство ISA, x86 развивается.
Некоторые ISA прерывают обратное сжатие, переопределяя существующие коды операций (например, MIPS64r6 сделал это), но это довольно редко. например, MIPS32r6 / MIPS64r6 является примером этого: https://en.wikipedia.org/wiki/MIPS_architecture переопределение нескольких кодировок, а также удаление нескольких инструкций.
x86 никогда не нарушал обратного компатита: Ryzen или Skylake-X все еще могли загружаться и запускать машинный код, который работал на 8086. Это часть того, что значит быть процессором x86: см. Также Начало x86: Intel 8080 против Intel 8086?, (Мы просто говорим о машинном коде, но даже устройства ввода-вывода эмулируются, если вы загружаете ПК в устаревшем режиме BIOS, а не в UEFI, поэтому очень ранняя ОС 8086 для ПК, как, например, ранняя DOS, может фактически работать естественным образом.)
Intel и AMD доводят это до такой степени, что недокументированные инструкции 8086, такие как SALC (например, sbb al,al
но без обновления FLAGS) все еще поддерживаются в 16- и 32-битном режиме на текущих процессорах, используя ценное пространство кодирования кода операции, которое можно использовать для более коротких кодировок для новых инструкций.
Но SW, который использует новые insns, работает только на новом HW. Новое программное обеспечение будет работать на текущем и будущем оборудовании, а старое оборудование - до тех пор, пока оно не станет совместимым. (например, в 32-битном коде, вы можете избежать использования cmov
или другие инструкции, впервые появившиеся в Pentium Pro, чтобы ваш код мог работать на P5 (i586) Pentium / PMMX.)
x86-64 устанавливает новую базовую линию, которая включает SSE2 и инструкции PPro, такие как cmov
, Так что, к счастью, 64-битному коду не нужно беспокоиться о совместимости со старыми процессорами, которые не имеют таких вещей, они необходимы для x86-64.
Новый базовый уровень, который включает AVX2, FMA и BMI2 (например, Haswell), был бы неплохо. BMI1/BMI2 особенно полезны, если ваш компилятор может использовать их повсюду в коде для более эффективных инструкций сдвига с переменным числом и т. Д., А не только в виде пары горячих циклов, как в инструкциях SIMD. Но Intel все еще продает новые процессоры без BMI2 (например, версии Skylake /Coffee Lake для Pentium /Celeron).
Если нет, то что происходит?
Инструкции, которые не поддерживаются процессором, обычно ошибаются #UD
(Не определено). В Unix-подобных ОС ваш процесс получит сигнал SIGILL (Недопустимая инструкция.
Единственный способ создать один двоичный файл, который будет использовать преимущества новых инструкций, но не будет вызывать сбои недопустимых команд на старых процессорах, - это обнаружение процессора во время выполнения и динамическая диспетчеризация. Некоторые компиляторы могут сделать это для вас.
Новые инструкции могут иметь кодировку, которая (на старых процессорах) выглядит как избыточный префикс для другой инструкции. например lzcnt
на процессоре, который не поддерживает его, будет декодироваться как rep bsr
, который работает как раз bsr
, И дает другой результат, чем lzcnt
!
(Документы Intel явно указывают на то, что будущие процессоры не гарантируют декодирование инструкций с бессмысленными префиксами так же, как это делают нынешние процессоры. Это оставляет им возможность создавать расширения ISA таким образом.)
Иногда безмолвное игнорирование бессмысленных префиксов REP на старых процессорах полезно для расширений ISA. например pause
является rep nop
, Очень полезно, что он безвредно декодирует на старых процессорах, позволяя помещать его в циклы без проверки. Точно так же аппаратное блокирование (транзакционная память) декодирует код, который все еще работает на старых процессорах, фактически делая атомарные операции вместо начала транзакции.
См. Также: https://www.agner.org/optimize/blog/read.php?i=25 Остановите войну с набором команд, автор Agner Fog. Некоторая история о том, что Intel перешагнула через AMD, не раскрывая подробностей о будущих расширениях ISA, поэтому AMD заканчивает разработку собственных несовместимых и тратит больше лет на добавление поддержки нового расширения для своих собственных процессоров. (Например, SSSE3 не был доступен на процессорах AMD до Bulldozer, а это означает, что даже игры, требующие компьютеров нового поколения, не могли требовать его в качестве базового уровня в течение многих лет.)
Но введены ли новые инструкции, чтобы разрешить то, что нельзя было сделать ранее?
Да, SIMD является одним из самых важных примеров. MMX, затем SSE/SSE2, затем SSE4.x. Тогда AVX для вдвое больше широких векторов. Параллельная обработка целого вектора из 16 или 32 байтов данных дает огромное ускорение для таких вещей, как strlen
или же memcmp
по сравнению с байтом за раз. Также очень полезно для многих вещей массива.
AVX2 Какой самый эффективный способ упаковать левый на основе маски? интересный пример новых трюков, включаемых новыми наборами команд например, AVX512 имеет эту встроенную операцию, в то время как AVX2 + BMI2 позволяет трюки с pdep
/ pext
это было невозможно раньше.
SSSE3 pshufb
является первой командой перемешивания с переменным управлением, и загрузка управления перемешиванием из справочной таблицы позволяет эффективно делать вещи, которые ранее были невозможны. Например, самый быстрый способ получить IPv4-адрес из строки.
Как реализовать atoi с помощью SIMD? также показывает некоторые изящные вещи, которые вы можете сделать с x86 pmaddubsw
/ pmaddwd
целочисленное умножение + горизонтальные инструкции добавления, чтобы умножить на десятичные значения-места.
Более ранняя история новых инструкций, добавляемых после 8086, хорошо документирована в старой версии руководства NASM, в приложении. В текущих версиях этого приложения удалены текстовые описания каждой инструкции, чтобы освободить место для SIMD-инструкций. (Их очень много.)
A.77 IMUL: Signed Integer Multiply
IMUL r/m8 ; F6 /5 [8086]
IMUL r/m16 ; o16 F7 /5 [8086]
IMUL r/m32 ; o32 F7 /5 [386]
IMUL reg16,r/m16 ; o16 0F AF /r [386]
IMUL reg32,r/m32 ; o32 0F AF /r [386]
IMUL reg16,imm8 ; o16 6B /r ib [286]
IMUL reg16,imm16 ; o16 69 /r iw [286]
IMUL reg32,imm8 ; o32 6B /r ib [386]
IMUL reg32,imm32 ; o32 69 /r id [386]
IMUL reg16,r/m16,imm8 ; o16 6B /r ib [286]
IMUL reg16,r/m16,imm16 ; o16 69 /r iw [286]
IMUL reg32,r/m32,imm8 ; o32 6B /r ib [386]
IMUL reg32,r/m32,imm32 ; o32 69 /r id [386]
Конечно, любая инструкция reg32 требует 386 для 32-битных расширений, но обратите внимание, что imul-немедленный был новым в 286 ( imul cx, [bx], 123
) в то время как 2-операнд imul был новым в 386 ( imul cx, [bx]
), позволяя умножать без ударов DX:AX, делая AX менее "особенным".
Другие 386 инструкции, такие как movsx
а также movzx
также проделал долгий путь к тому, чтобы сделать регистры более ортогональными, позволяя эффективно расширять вход в любой регистр. Перед этим вы должны были перенести свои данные в AL и использовать cbw
или в AX для cwd
подписать расширение в DX:AX.