Предположим, у меня есть программа для выполнения операций AES.
Некоторые продвинутые процессоры имеют набор команд AES-NI, а другие — нет.
Должен ли я скомпилировать мою программу в два исполняемых файла: A_with_aes_ni.exe и B_without_aes_ni.exe?
То, что вы хотите, называется диспетчером ЦП. Агнер Фог имеет 10 страниц текста по этому вопросу в третьей главе «Создание критического кода в нескольких версиях для разных наборов команд» своего Руководство по оптимизации C ++ . Он обсуждает это как с GCC, так и с ICC.
Вам нужен только один исполняемый файл, но вам нужно скомпилировать два разных объектных файла с включенной AES и без нее. Затем диспетчер определяет, какой набор инструкций доступен, и выбирает путь кода на основании этого.
Я пытался сделать это с MSVC2010 Диспетчер процессора для Visual Studio для AVX и SSE но не удалось. Я подозреваю, что мог бы заставить это работать теперь все же.
Редактировать:
В Agner Fog’s vectorclass у него есть файл dispatch_example.cpp
а также instrset_detech.cpp
который должен иметь большую часть того, что нужно сделать диспетчеру. Вам все еще нужно выяснить, как определить, есть ли у процессора AES. Вам необходимо увеличить файл intrset_detect.cpp. В соответствии с википедия когда вы читаете CPUID, бит 23 в регистре ECX устанавливается, если у CPU есть AES. В Википедии также есть примеры кода для чтения CPUID (помимо instrset_detech.cpp
— еще один хороший пример в https://github.com/Mysticial/Flops в файле cpuid.c)
Один из способов сделать это в Solaris — это иметь библиотеки аппаратных возможностей,
которые динамически загружаются компоновщиком во время выполнения.
Другой вариант — сначала загрузить обработчик прерываний для недопустимых инструкций, а затем проверить нужные инструкции на машинном языке. Если вы попали в ловушку,
тогда вы знаете, что не можете использовать оптимизированную версию и должны загружать неоптимизированную (или менее оптимизированную).
Хотя мне нравится предложение Эндрю выше, я думаю, что безопаснее проверить
конкретные инструкции, которые вам нужны. Таким образом, вам не нужно постоянно обновлять
ваше приложение для более нового вывода CPUID.
Отредактировано, чтобы добавить:
Я понимаю, что должен был привести пример. Для Соляриса libc на x64
платформу, мы предоставляем hw-оптимизированные версии библиотеки — три для
32 бита, один для 64 бита. Мы можем увидеть различия, запустив elfdump -H
на интересующий файл:
s11u1:jmcp $ elfdump -H /usr/lib/libc/libc_hwcap1.so.1
Capabilities Section: .SUNW_cap
Object Capabilities:
index tag value
[0] CA_SUNW_HW_1 0x86d [ SSE MMX CMOV SEP CX8 FPU ]
Symbol Capabilities:
index tag value
[2] CA_SUNW_ID hrt
[3] CA_SUNW_HW_1 0x40002 [ TSCP TSC ]
Symbols:
index value size type bind oth ver shndx name
[1] 0x000f306c 0x00000225 FUNC LOCL D 0 .text gettimeofday%hrt
[2] 0x000f2efc 0x00000165 FUNC LOCL D 0 .text gethrtime%hrt
Capabilities Chain Section: .SUNW_capchain
Capabilities family: gettimeofday
chainndx symndx name
1 [702] gettimeofday
2 [1] gettimeofday%hrt
Capabilities family: gethrtime
chainndx symndx name
4 [1939] gethrtime
5 [2] gethrtime%hrt
s11u1:jmcp $ elfdump -H /usr/lib/libc/libc_hwcap2.so.1
Capabilities Section: .SUNW_cap
Object Capabilities:
index tag value
[0] CA_SUNW_HW_1 0x1875 [ SSE2 SSE MMX CMOV AMD_SYSC CX8 FPU ]
Symbol Capabilities:
index tag value
[2] CA_SUNW_ID hrt
[3] CA_SUNW_HW_1 0x40002 [ TSCP TSC ]
Symbols:
index value size type bind oth ver shndx name
[1] 0x000f253c 0x00000225 FUNC LOCL D 0 .text gettimeofday%hrt
[2] 0x000f23cc 0x00000165 FUNC LOCL D 0 .text gethrtime%hrt
Capabilities Chain Section: .SUNW_capchain
Capabilities family: gettimeofday
chainndx symndx name
1 [702] gettimeofday
2 [1] gettimeofday%hrt
Capabilities family: gethrtime
chainndx symndx name
4 [1939] gethrtime
5 [2] gethrtime%hrt
Угадайте, что из перечисленного выше для систем AMD, а что для Intel?
У компоновщика Solaris есть смарты для загрузки правильной библиотеки hwcap во время выполнения
до того, как ваш процесс ‘_init () вызывается.