Для подсчета постоянных сдвигов во время компиляции вы можете получить неплохие результаты. В противном случае это не так.
Это всего лишь реализация SSE кода r0
/r1
с вашего вопроса, так как нет другого очевидного способа сделать это. Сдвиги с переменным числом доступны только для бит-сдвигов в векторных элементах, а не для смещений байтов всего регистра. Таким образом, мы просто переносим низкие 64 бит до максимума 64 и используем сдвиг с переменным числом, чтобы поместить их в нужное место.
// untested
#include <immintrin.h>
/* some compilers might choke on slli/srli with non-compile-time-constant args
* gcc generates the xmm, imm8 form with constants,
* and generates the xmm, xmm form with otherwise. (With movd to get the count in an xmm)
*/
// doesn't optimize for the special-case where count%8 = 0
// could maybe do that in gcc with if(__builtin_constant_p(count)) { if (!count%8) return ...; }
__m128i mm_bitshift_left(__m128i x, unsigned count)
{
__m128i carry = _mm_bslli_si128(x, 8); // old compilers only have the confusingly named _mm_slli_si128 synonym
if (count >= 64)
return _mm_slli_epi64(carry, count-64); // the non-carry part is all zero, so return early
// else
carry = _mm_srli_epi64(carry, 64-count); // After bslli shifted left by 64b
x = _mm_slli_epi64(x, count);
return _mm_or_si128(x, carry);
}
__m128i mm_bitshift_left_3(__m128i x) { // by a specific constant, to see inlined constant version
return mm_bitshift_left(x, 3);
}
// by a specific constant, to see inlined constant version
__m128i mm_bitshift_left_100(__m128i x) { return mm_bitshift_left(x, 100); }
Я думал, что это будет менее удобно, чем оказалось. _mm_slli_epi64
работает с gcc/clang/icc, даже если счетчик не является константой времени компиляции (генерирует movd
из целочисленной reg в xmm reg). Существует _mm_sll_epi64 (__m128i a, __m128i count)
(обратите внимание на недостаток i
), но, по крайней мере, в наши дни, внутренний код i
может генерировать любую форму psllq
.
во время компиляции постоянной версии счета являются довольно эффективным, compiling to 4 instructions (или 5 без AVX):
mm_bitshift_left_3(long long __vector(2)):
vpslldq xmm1, xmm0, 8
vpsrlq xmm1, xmm1, 61
vpsllq xmm0, xmm0, 3
vpor xmm0, xmm0, xmm1
ret
Performance:
Это имеет 3 цикла задержки (vpslldq (1) - > vpsrlq (1) -> vpor (1)) на Intel SnB/IvB/Haswell, пропускная способность которого ограничена одним на 2 цикла (насыщение блока сдвига вектора на порте 0). Байт-сдвиг работает на блоке тасования на другом порту. Сдвиг векторов немедленного счета - это все одноуровневые инструкции, так что это всего лишь 4 слитых домена, которые занимают пространство конвейера, когда смешиваются с другим кодом. (Variable-счета векторные сдвиги 2 моп, 2 цикла задержки, так что переменная-счетчик версии этой функции хуже, чем она выглядит из подсчета инструкций.)
Или для подсчетов> = 64:
mm_bitshift_left_100(long long __vector(2)):
vpslldq xmm0, xmm0, 8
vpsllq xmm0, xmm0, 36
ret
Если ваш счетчик смены не константа времени компиляции, вам нужно разветвить счетчик> 64, чтобы выяснить, следует ли сдвигать перенос влево или вправо.Я считаю, что значение сдвига интерпретируется как целое число без знака, поэтому отрицательный счет невозможно.
Также требуются дополнительные инструкции для подсчета int
и 64-счета в векторные регистры. Выполнение этого безрассудным способом с векторным сравнением и инструкцией по смешиванию может быть возможно, но ветка, вероятно, хорошая идея.
версия переменного счетчик для __uint128_t
в регистрах GP выглядит довольно хорошо; лучше, чем версия SSE. Clang does a slightly better job than gcc, emitting fewer mov
instructions, но он по-прежнему использует две команды cmov
для счета count = = 64. (. Поскольку команды x86 целочисленный сдвиг маскировать счет, а не насыщением)
__uint128_t leftshift_int128(__uint128_t x, unsigned count) {
return x << count; // undefined if count >= 128
}
Если 'M' случается быть кратным 8 битам, и у вас есть SSSE3, вам повезло:' palignr'. Если нет, он становится уродливым, и вам действительно нужно делать сдвиги, AND, тасования и OR. –
См. Http://stackoverflow.com/questions/9980801/looking-for-sse-128-bit-shift-operation-for-non-immediate-shift-value –
Выполняете ли вы обработку битовых потоков или арифметических переменных (ints, поплавки и т. д.)? – bazza