gcc (6.1.0), использующий "неправильные" инструкции во встроенных функциях SSE
Предыстория: я разрабатываю вычислительно-интенсивный инструмент, написанный на C/C++, который должен работать на различных процессорах x86_64. Чтобы ускорить вычисления, которые являются как плавающими, так и целочисленными, код содержит довольно много встроенных функций SSE* с разными путями, приспособленными к различным возможностям CPU SSE. (Поскольку флаги ЦП обнаруживаются в начале программы и используются для установки логических значений, я предположил, что предсказание ветвления для адаптированных блоков кода будет работать очень эффективно).
Для простоты я предположил, что нужно рассматривать только SSE2 до SSE4.2.
Чтобы получить доступ к SSE4.2 intrinsics для путей 4.2, мне нужно использовать опцию -ccsems.2 для gcc.
Проблема Проблема, с которой я столкнулся, заключается в том, что, по крайней мере, с 6.1.0, gcc идет и реализует встроенную функцию sse2, mm_cvtsi32_si128, с инструкцией sse4.2, pinsrd.
Если я ограничу компиляцию с помощью -msse2, она будет использовать инструкцию sse2, movd, т.е. тот, который Intel "Intrinsics Guide" говорит, что он должен использовать.
Это раздражает по двум причинам.
1) Критическая проблема заключается в том, что теперь программа вылетает с недопустимой инструкцией, когда запускается на процессоре pre4.2. У меня нет контроля над тем, какой HW используется, поэтому исполняемый файл должен быть совместим со старыми машинами, но при этом должен использовать преимущества новых HW, где это возможно.
2) Согласно руководству Intel по встроенным функциям, команда pinsrd намного медленнее, чем команда mov, которую она заменяет. (pinsrd более общий, но в этом нет необходимости).
Кто-нибудь знает, как заставить gcc просто использовать инструкции, которые, как говорится в руководстве по встроенным функциям, следует использовать, но при этом разрешать доступ ко всем SSE2 через SSE4* в одном модуле компиляции?
Обновление: я также должен отметить, что один и тот же код скомпилирован под Linux, Windows и OSX с использованием различных компиляторов, поэтому он хотел бы избегать или, по крайней мере, иметь наименьшее количество специфичных для компилятора расширений, если это возможно.
Обновление 2: (благодаря @PeterCordes) Похоже, что если оптимизация включена, gcc вернется к использованию movd из pinsrd, где это необходимо.
1 ответ
Если вы даете -msse4.2
Отметьте в командной строке gcc на этапе компиляции, он будет предполагать, что он может свободно использовать вплоть до набора команд SSE 4.2 для всей единицы перевода. Это может привести к поведению, которое вы описали. Если вам нужен код, который использует только код SSE2 и ниже, используйте -msse2
(или нет флага вообще, если вы создаете для x86_64) не требуется.
Некоторые варианты, которые я могу придумать:
Если вы можете легко разбить ваш код на функциональном уровне, то может помочь функция многовариантности gcc. Это требует относительно недавней версии компилятора, но позволяет вам делать такие вещи (взято по ссылке выше):
__attribute__ ((target ("default"))) int foo () { // The default version of foo. return 0; } __attribute__ ((target ("sse4.2"))) int foo () { // foo version for SSE4.2 return 1; } __attribute__ ((target ("arch=atom"))) int foo () { // foo version for the Intel ATOM processor return 2; } __attribute__ ((target ("arch=amdfam10"))) int foo () { // foo version for the AMD Family 0x10 processors. return 3; } int main () { int (*p)() = &foo; assert ((*p) () == foo ()); return 0; }
В этом примере gcc автоматически скомпилирует разные версии
foo()
и отправлять к соответствующему во время выполнения в зависимости от возможностей процессора.Вы можете разбить различные реализации (SSE2, SSE4.2 и т. Д.) На разные блоки перевода, а затем выполнить диспетчеризацию для правильной реализации во время выполнения.
Вы можете поместить весь код SIMD в общую библиотеку и создать общую библиотеку несколько раз с разными флагами компилятора. Затем во время выполнения вы можете определить возможности ЦП и загрузить соответствующую версию общей библиотеки. Этот подход используется библиотеками, такими как Intel Math Kernel Library.