GCC и clang требуют, чтобы вы включили все используемые вами расширения . В противном случае это ошибка во время компиляции, например ошибка: во время вставки не удалось вызвать always_inline error: inlining failed in call to always_inline ‘__m256d _mm256_mask_loadu_pd(__m256d, __mmask8, const void*)’: target specific option mismatch
Использование -march=haswell
или чего-либо другого предпочтительнее, чем включение определенных расширений, потому что это также устанавливает соответствующие параметры настройки. И вы не забудете такие полезные, как -mpopcnt
, которые позволят std::bitset::count()
встроить инструкцию popcnt
и сделают все изменения числа переменных более эффективными с BMI2 shlx
/ shrx
(1 моп против 3)
MSVC и ICC этого не делают, и позволят вам использовать встроенные функции для выдачи инструкций, с которыми они не могли автоматически векторизоваться.
Вы должны обязательно включить AVX, если вы используете встроенные функции AVX. Я думаю, что читал / видел, что без этого MSVC не всегда будет использовать vzeroupper
там, где должен.
Для компиляторов, которые поддерживают расширения GNU (GCC, clang, ICC), вы можете использовать такие вещи, как __attribute__((target("avx")))
, для определенных функций в модуле компиляции. Или лучше, __attribute__((target("arch=haswell")))
, чтобы также установить параметры настройки. (Но это также позволяет использовать AVX2 и FMA, которые вам могут не понадобиться. Я не уверен, могут ли атрибуты target
установить -mtune=xx
)
https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes (а также
__attribute__((target()))
предотвратит их встраивание в функции с другими целевыми параметрами, поэтому будьте осторожны, чтобы использовать это для функций, в которые они будут встроены, если сама функция слишком мала.
Смотри также
https://gcc.gnu.org/wiki/FunctionMultiVersioning для использования различных целевых опций в нескольких определениях одного и того же имени функции , для поддерживаемой компилятором диспетчеризации во время выполнения. Но я не думаю, что есть портативный (для MSVC) способ сделать это.
С MSVC вам ничего не нужно, хотя, как я уже сказал, я думаю, что обычно плохая идея использовать встроенные AVX без -arch:AVX
, так что вам лучше было бы поместить их в отдельный файл. Но для AVX против AVX2 + FMA или SSE2 против SSE4.2 все в порядке без всего.
Просто #define AVX2_FUNCTION
до пустой строки вместо __attribute__((target("avx2,fma")))
* * +1055 например,
#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
// apparently ICC doesn't support target attributes
#define TARGET_HASWELL __attribute__((target("arch=haswell")))
#else
#define TARGET_HASWELL // empty
// maybe warn if __AVX__ isn't defined for functions where this is used?
// if you need to make sure MSVC uses vzeroupper everywhere needed.
#endif
TARGET_HASWELL
void foo_avx(float *__restrict dst, float *__restrict src) {
__m256 v = _mm256_loadu_ps(src);
...
...
}
С GCC и clang макрос расширяется до __attribute__((target))
; с MSVC и ICC это не так.
ICC Pragma:
https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-optimization-parameter документирует прагму, которую вы хотите поместить перед функциями AVX, чтобы убедиться, что vzeroupper используется должным образом в функциях, которые используют _mm256
встроенные функции.
#pragma intel optimization_parameter target_arch=AVX
Для ICC вы можете использовать #define TARGET_AVX
и всегда использовать его в отдельной строке перед функцией, где вы можете поставить __attribute__
или прагму. Вы также можете захотеть использовать отдельные макросы для определения и объявления функций, если ICC не хочет этого в объявлениях. И макрос для завершения блока функций AVX, если вы хотите, чтобы после них были функции, отличные от AVX. (Для компиляторов не-ICC это будет пустым.)