Ответ 1
Почти каждая _ss
и _ps
внутренняя/инструкция имеет версию double
с суффиксом _sd
или _pd
. (Scalar Double или Packed Double).
Например, выполните поиск (double
встроенного искателя Intel, чтобы найти встроенные функции, которые принимают double
как первый аргумент. Или просто выясните, какой оптимальный будет asm, а затем просмотрите внутренности для этих инструкций в руководстве insn ref. Кроме того, что не перечисляет все встроенные функции для movsd
, поэтому поиск имени команды в поисковом устройстве intrinsics часто работает.
re: файлы заголовков: всегда включайте <immintrin.h>
. Он включает все встроенные функции Intel SSE/AVX.
См. также способы поместить float
в вектор, а sse теги wiki для ссылок о том, как перетасовывать векторы. (т.е. таблицы команд перетасовки в Agner Fog оптимизация руководства по сборке)
(см. ниже ссылку godbolt на какой-то интересный вывод компилятора)
re: ваша последовательность
Используйте только _mm_move_ss
(или sd), если вы действительно хотите объединить два вектора.
Вы не показываете, как определяется m
. Использование a
в качестве имени переменной для float и вектора означает, что единственной полезной информацией в векторе является arg float
. Разумеется, смена имени переменной означает, что она не компилируется.
К сожалению, похоже, что нет никакого способа просто "отличить" float
или double
от вектора с мусором в верхних 3 элементах, например, для __m128
→ __m256
: __m256 _mm256_castps128_ps256 (__m128 a)
. Я задал новый вопрос об этом ограничении с помощью intrinsics: Как слить скаляр в вектор без компилятора, теряющего инструкцию обнуления верхних элементов? Ограничение разработки встроенных функций Intel?
Я попытался использовать _mm_undefined_ps()
для достижения этого, надеясь, что это подскажет в компиляторе, что он может просто оставить входящий высокий мусор на месте, в
// don't use this, it doesn't make better code
__m128d double_to_vec_highgarbage(double x) {
__m128d undef = _mm_undefined_pd();
__m128d x_zeroupper = _mm_set_sd(x);
return _mm_move_sd(undef, x_zeroupper);
}
но clang3.8 компилирует его в
# clang3.8 -O3 -march=core2
movq xmm0, xmm0 # xmm0 = xmm0[0],zero
ret
Таким образом, нет преимущества, но при этом обнуляя верхнюю половину, вместо того, чтобы скомпилировать ее только как ret
. gcc действительно делает довольно плохой код:
double_to_vec_highgarbage: # gcc5.3 -march=nehalem
movsd QWORD PTR [rsp-16], xmm0 # %sfp, x
movsd xmm1, QWORD PTR [rsp-16] # D.26885, %sfp
pxor xmm0, xmm0 # __Y
movsd xmm0, xmm1 # tmp93, D.26885
ret
_mm_set_sd
представляется лучшим способом превратить скаляр в вектор.
__m128d double_to_vec(double x) {
return _mm_set_sd(x);
}
clang компилирует его в movq xmm0,xmm0
, gcc для сохранения/перезагрузки с помощью -march=generic
.
Другие интересные выходы компилятора из версий float
и double
в проводнике компилятора Godbolt
float_to_vec: # gcc 5.3 -O3 -march=core2
movd eax, xmm0 # x, x
movd xmm0, eax # D.26867, x
ret
float_to_vec: # gcc5.3 -O3 -march=nehalem
insertps xmm0, xmm0, 0xe # D.26867, x
ret
double_to_vec: # gcc5.3 -O3 -march=nehalem. It could still have use movq or insertps, instead of this longer-latency store-forwarding round trip
movsd QWORD PTR [rsp-16], xmm0 # %sfp, x
movsd xmm0, QWORD PTR [rsp-16] # D.26881, %sfp
ret
float_to_vec: # clang3.8 -O3 -march=core2 or generic (no -march)
xorps xmm1, xmm1
movss xmm1, xmm0 # xmm1 = xmm0[0],xmm1[1,2,3]
movaps xmm0, xmm1
ret
double_to_vec: # clang3.8 -O3 -march=core2, nehalem, or generic (no -march)
movq xmm0, xmm0 # xmm0 = xmm0[0],zero
ret
float_to_vec: # clang3.8 -O3 -march=nehalem
xorps xmm1, xmm1
blendps xmm0, xmm1, 14 # xmm0 = xmm0[0],xmm1[1,2,3]
ret
Таким образом, как clang, так и gcc используют разные стратегии для float
vs. double
, даже если они могут использовать ту же стратегию.
Использование целочисленных операций типа movq
между операциями с плавающей запятой вызывает дополнительную задержку задержки байпаса. Используя insertps
в ноль, верхние элементы входного регистра должны быть лучшей стратегией для float или double, поэтому все компиляторы должны использовать это, когда SSE4.1 доступен. xorps + blend тоже хорош и может работать на большем количестве портов, чем вставки. Магазин/перезагрузка, вероятно, является наихудшим, если только мы не узкополосны по пропускной способности ALU, и латентность не имеет значения.