Обычным способом было бы 2 инструкции по одной операции, 9 байтов.
mov $0xaaaa, %eax # 5 bytes
kmovw %eax, %k7 # 4 bytes
Вы можете выбрать любой регистр нуля; это не должно быть EAX. Но для r8d..r15d потребуется префикс REX для mov
, который стоит 1 дополнительный байт размера кода. (3-байтовый префикс VEX на kmovw
уже включает биты REX, поэтому он не изменит размер.)
Когда вы не уверены, вы можете и должны взглянуть на то, что C компиляторы делают. Например, скомпилируйте с -O3
return _mm512_maskz_add_ps (0xaaaa, a, b)
в функции с __m512
аргументами и посмотрите, как она получает 0xaaaa
в регистр.
G CC, clang и я CC не всегда создаю оптимальный код, поэтому, если он выглядит неоптимальным, вы могли бы найти пропущенную оптимизацию в компиляторе. Проверьте https://uops.info/ и руководство по микроарху Agner Fog, чтобы увидеть, есть ли причина для выбора.
add eax, imm32
уже 5 байтов, то же самое длина как mov eax, imm32
. (И обратите внимание, что есть 5-байтовая mov r32, imm32
форма без модема для каждого регистра, не только для EAX).
xor-zero + add - это 100% бессмысленно. В 32-битном режиме (где доступен 1-байтовый inc
/ dec
, xor-zero + inc
иногда используется при оптимизации по размеру кода по скорости, но даже add r32, imm8
равен 3 байта.
Даже для небольшой (8-битной) константы вы бы сэкономили только 1 байт с xor-zero + 2-байтом mov al, imm8
, но по цене 2 мопа для внешнего интерфейса вместо 1 для mov r32, imm32
. Производительность - это причина, по которой компиляторы обычно используют mov $1, %eax
вместо немного более короткой 2-байтовой последовательности или 3-байтовой push imm8 / pop
, которая еще короче, но еще менее эффективна. См. Также CodeGolf Советы по машинному коду .SE x86-64 для компактных способов помещения констант в регистры, например, если у вас уже есть нулевой регистр, вы можете использовать 3-байтовый LEA, чтобы получить любое значение от -128 до +127 в другое Это всего 1 моп, но он не может работать на таком количестве портов, как mov
-mmediate.
Обычно размер кода в байтах следует рассматривать только как разрыв ie между последовательностями которые декодируют до как можно меньшего числа мопов. См. https://agner.org/optimize/ и * 10 54 *
Подсчет в инструкциях часто не актуален. Но на Xeon Phi (Knight's Landing) важно избегать многопользовательских инструкций. Декодеры останавливаются на несколько циклов, когда одна инструкция должна декодировать до более чем 1 мегапикселя.
Массивные "большие ядра" x86-процессоры имеют кэш-память uops (Intel начиная с Sandybridge, AMD начиная с Zen), которая в основном удаляет декодирование узкие места для горячих петель. (Настройка для более ранних ISA включала заботу о выравнивании и 16-байтовых блоках выборки, а также о том, будет ли многопользовательская инструкция соответствовать первому декодеру или ждать следующего цикла; см. Разделы Core2 / Nehalem в микроархе Agner Fog PDF.)
Некоторые инструкции по-разному декодируются на разных uarches, поэтому ваш JIT может декодировать между последовательностями на основе текущей цели.
Загрузка константы из памяти: не стоит он
kmovw (mem), %k7
кодируется, но, к сожалению, на Skylake-X / Каскадное озеро / Ледяное озеро он декодируется до 2 моп , как mov
загрузка в EAX + a kmov
. Таким образом, для порта 5 все еще требуется uop. https://www.uops.info/html-instr/KMOVW_K_M16.html
Он также практически ничего не сохраняет для размера кода: для режима адресации RIP-относительной требуется 4 байта rel32
за пределами кода операции + modrm, то есть за пределами того, что понадобится источнику EAX. Таким образом, kmovw constant(%rip), %k7
будет всего 8 байт, всего на 1 байт короче, чем mov-немедленный + kmov eax, k1.
Кроме того, вам все равно нужно поместить 2 байта данных в пул констант где-нибудь вместе с любые векторные константы.
Но вы, как правило, хотите загружать векторные константы из памяти; они слишком велики, чтобы их можно было эффективно построить из немедленных + тасовок, если только это не повторяющийся шаблон, который вы можете сделать с mov $imm32, %eax
/ vpbroadcastd %eax, %zmm0
. Или повторяющийся шаблон, который вы можете создать на лету из регистра «все единицы» из vpcmpeqd %ymm0, %ymm0, %ymm0
или vpternlogd %zmm0, %zmm0, %zmm0, 0xff
.
SIMD векторных нагрузок, эффективны; один моп. Только загрузки регистра маски неэффективны.