Этот ответ чисто о случае постоянной C
. Если у вас есть неконстантные входные данные, важно, откуда они берутся (память, регистры, недавние вычисления, которые вы, возможно, могли бы в первую очередь выполнять в векторных регистрах?) И, возможно, что вы делаете с результирующим вектор. Перетасовывание отдельных скалярных переменных в / из SIMD-векторов своего рода отстой, с компромиссом между узкими местами порта ALU в сравнении с задержкой и пропускной способностью сохранения / перезагрузки (и задержки пересылки магазина для скалярного -> вектора). Функция сохранения / перезагрузки хороша для получения множества мелких элементов из вектора SIMD, когда вы хотите их все.
Для констант C_a
и C_b
, даже MSV C хорошо справляется с постоянным распространением через это _mm_set
. Таким образом, нет никакого преимущества в написании инициализирующего c инициализатора, такого как Ошибка SSE - Использование m128i_i32 для определения полей переменной __m128i
Помните, что реальный определитель производительности сборка, которую вы можете заставить компилятор создавать, но не то, какие встроенные функции вы используете для этого.
#include <immintrin.h>
__m128i xor_const(__m128i v) {
return _mm_xor_si128(v, _mm_set_epi64x(0x789abc, 0x123456));
}
Скомпилировано ( на Godbolt ) с помощью x64 MSV C -O2 Gv (чтобы использовать vectorcall, чтобы мы могли видеть, что он делает, когда вектор уже находится в регистре, например, когда он встроен), мы получаем этот довольно глупый ассемблер, который, надеюсь, не будет таким плохим в большая функция после встраивания:
;; MSVC 19.10
;; this is in the .rdata section; godbolt just filters directives that aren't interesting
;; "everyone knows" that compilers put data in the right sections
__xmm@0000000000789abc0000000000123456 DB 'V4', 012H, 00H, 00H, 00H, 00H, 00H
DB 0bcH, 09aH, 'x', 00H, 00H, 00H, 00H, 00H
xor_const@@16 PROC ; COMDAT
movdqa xmm1, XMMWORD PTR __xmm@0000000000789abc0000000000123456
pxor xmm1, xmm0
movdqa xmm0, xmm1
ret 0
xor_const@@16 ENDP
Мы можем видеть, что _mm_set
intrinsi c скомпилирован в 16-байтовую константу в хранилище stati c, как мы и хотим. Отказ от использования pxor xmm0, xmm1
удивителен, но MSV C хорошо известен для asm, который часто не так хорош по сравнению с G CC и / или лязгом. Опять же, как часть большой функции, когда у нее есть выбор регистров, у нас, вероятно, не будет лишних movdqa
. И если xor был в al oop, то загрузка в любом случае за пределами al oop - это то, что нам нужно. Это была не самая последняя версия MSV C; Godbolt имеет только самые последние версии MSV C, установленные для C ++, а не C, но вы пометили это C.
Для сравнения, GCC9.2 -O3 компилируется к ожидаемому источнику памяти PXOR, который эффективен на всех процессорах.
xor_const:
pxor xmm0, XMMWORD PTR .LC0[rip]
ret
.section .rodata # Godbolt strips out stuff like section directive; re-added manually
.LC0:
.quad 1193046
.quad 7903932
Возможно, вы могли бы получить компилятор для генерации того же ассемблера с массивом stati c alignas(16)
, содержащим константу, и _mm_load_si128()
от этого. Но зачем?
Одна вещь, которую следует избегать пишет: static const __m128i C = _mm_set...
- компиляторы очень глупы и не будут складывать _mm_set
в состояние c постоянный инициализатор для __m128i
. C компиляторы откажутся компилировать не константный инициализатор stati c. Компиляторы C ++ зарезервируют некоторое пространство BSS и запустят функцию, подобную конструктору, для копирования из постоянной только для чтения в это пространство BSS, потому что _mm_set
не полностью оптимизирует в этом случае.