Допустим, у меня есть программа для выполнения операций AES.
Некоторые продвинутые ЦП имеют набор инструкций AES-NI, а другие ЦП не имеют.
Должен ли я компилировать свою программу в два исполняемых файла: A_with_aes_ni.exe и B_without_aes_ni.exe?
Допустим, у меня есть программа для выполнения операций AES.
Некоторые продвинутые ЦП имеют набор инструкций AES-NI, а другие ЦП не имеют.
Должен ли я компилировать свою программу в два исполняемых файла: A_with_aes_ni.exe и B_without_aes_ni.exe?
То, что вам нужно, называется диспетчером ЦП. У Агнера Фога есть 10 страниц текста на эту тему в третьей главе «Создание критического кода в нескольких версиях для разных наборов инструкций» его Руководство по оптимизации C++ . Он обсуждает это как с GCC, так и с ICC.
Вам нужен только один исполняемый файл, но вам нужно скомпилировать два разных объектных файла с включенным AES и без него. Затем диспетчер определяет, какой набор инструкций доступен, и на его основе выбирает путь кода.
Я попытался сделать это с помощью MSVC2010 диспетчера процессора для визуальной студии для AVX и SSE, но безуспешно. Я подозреваю, что я мог бы заставить его работать сейчас.
Изменить: в vectorclass Агнера Фога есть файл dispatch_example.cpp
и instrset_detech.cpp
, в котором должна быть большая часть того, что вы нужно сделать диспетчер. Вам все еще нужно выяснить, как определить, имеет ли процессор AES. Вам необходимо дополнить файл intrset_detect.cpp. Согласно википедии, когда вы читаете бит CPUID 23 в регистре ECX, устанавливается, если процессор имеет AES. В Википедии также есть примеры кода для чтения CPUID (помимо instrset_detech.cpp
— еще один хороший пример находится на https://github.com/Mysticial/Flops в файле cpuid.c)
Один из способов сделать это в Solaris — иметь библиотеки аппаратных возможностей, которые динамически загружаются компоновщиком во время выполнения.
Другой вариант — сначала загрузить обработчик ловушек для недопустимых инструкций, а затем проверить инструкции на желаемом машинном языке. Если вы попали в ловушку, то вы знаете, что не можете использовать оптимизированную версию и должны загрузить неоптимизированную (или менее оптимизированную).
Хотя мне нравится предложение Эндрю выше, я думаю, что безопаснее проверить конкретные инструкции, которые вам нужны. Таким образом, вам не нужно постоянно обновлять свое приложение для получения новых выходных данных CPUID.
Отредактировано для добавления: я понимаю, что должен был привести пример. Для libc Solaris на платформе x64 мы предоставляем аппаратно-оптимизированные версии библиотеки — три для 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() вашего процесса.