128-битный счетчик SSE?
-
27-10-2019 - |
Вопрос
Мне нужна функция переменной __m128i, которая имеет период 2^128. Это не нужно монотонно увеличивать (как счетчик), но посетите каждое значение один раз.
Самый простой пример, о котором я мог бы подумать, на самом деле-128-битный счетчик, но мне было трудно реализовать в SSE. Есть ли какие -нибудь более простые/более быстрые решения?
Решение
Вот монотонный счетчик. Я не уверен, может ли вы назвать это простым, хотя.
Предполагая оба ONE
а также ZERO
всегда в регистрах, тогда это должно составлять 5 инструкций. (7 или 8, если кодирование VEX не используется)
inline __m128i nextc(__m128i x){
const __m128i ONE = _mm_setr_epi32(1,0,0,0);
const __m128i ZERO = _mm_setzero_si128();
x = _mm_add_epi64(x,ONE);
__m128i t = _mm_cmpeq_epi64(x,ZERO);
t = _mm_and_si128(t,ONE);
t = _mm_unpacklo_epi64(ZERO,t);
x = _mm_add_epi64(x,t);
return x;
}
Тестовый код (MSVC):
int main() {
__m128i x = _mm_setr_epi32(0xfffffffa,0xffffffff,1,0);
int c = 0;
while (c++ < 10){
cout << x.m128i_u64[0] << " " << x.m128i_u64[1] << endl;
x = nextc(x);
}
return 0;
}
Выход:
18446744073709551610 1
18446744073709551611 1
18446744073709551612 1
18446744073709551613 1
18446744073709551614 1
18446744073709551615 1
0 2
1 2
2 2
3 2
Немного лучшая версия, предложенная @Norbert P. Это экономит 1 инструкцию по моему первоначальному решению.
inline __m128i nextc(__m128i x){
const __m128i ONE = _mm_setr_epi32(1,0,0,0);
const __m128i ZERO = _mm_setzero_si128();
x = _mm_add_epi64(x,ONE);
__m128i t = _mm_cmpeq_epi64(x,ZERO);
t = _mm_unpacklo_epi64(ZERO,t);
x = _mm_sub_epi64(x,t);
return x;
}
Другие советы
Никогда не забывайте принцип поцелуя.
Вставка этого (не знаковые целые числа должны обернуться стандартом C, следовательно, посещение каждого значения только один раз):
__uint128_t inc(__uint128_t x) {
return x+1;
}
в это урожайность (для x64):
addq $1, %rdi
adcq $0, %rsi
movq %rdi, %rax
movq %rsi, %rdx
ret
Легко/достаточно быстро? Если вы встроете это, вы, вероятно, сможете сойти с рук только addq
/adcq
( movq
песок ret
требуются X64 ABI: если вы вписали функцию, они не требуются)
Чтобы рассмотреть комментарий Voo о башне MSVC, вы можете использовать следующее:
inline void inc(unsigned long long *x, unsigned long long *y) {
if (!++*x) ++*y; // yay for obfuscation!
}
У меня нет установки MSVC поблизости, поэтому я не могу ее проверить, но это должен Получите что -то похожее на то, что я опубликовал выше. Тогда, если вы В самом деле нужен __m128i, вы должны иметь возможность В ролях две половинки.