#c #optimization #sse #avx #scramble
#c #оптимизация #sse #avx #скремблирование
Вопрос:
Если bitchar[]
это массив из 0 и 1, я хочу перевернуть знак in[i]
if bitchar[i] = 1
(скремблирование):
float *in = get_in();
float *out = get_out();
char *bitchar = get_bitseq();
for (int i = 0; i < size; i ) {
out[i] = in[i] * (1 - 2 * bitchar[i]);
}
Мой код AVX:
__m256 xmm1 = _mm256_set_ps(1);
__m256 xmm2 = _mm256_set_ps(2);
for (int i = 0; i < size; i =8) {
__m256 xmmb = _mm256_setr_ps (bitchar[i 7], bitchar[i 6], bitchar[i 5], bitchar[i 4], bitchar[i 3], bitchar[i 2], bitchar[i 1], bitchar[i]);
__m256 xmmpm1 = _mm256_sub_ps(xmm1, _mm256_mul_ps(xmm2,xmmb));
__m256 xmmout = _mm256_mul_ps(_mm256_load_ps(amp;in[i]),xmmpm1);
_mm256_store_ps(amp;out[i],xmmout);
}
Однако код AVX не намного быстрее, иногда даже медленнее. Возможно, мой avx не является оптимальным. Кто-нибудь может мне помочь?
Комментарии:
1. чтобы переключить знак, вам просто нужно XOR значение с 0x80000000, используя
_mm256_xor_ps
вместо множества подобных инструкций2. @AdrianMole да, это так
in[i]
3. @phuclv спасибо, я согласен. Однако прием xor зависит от того, как бит знака реализован в конкретной архитектуре. Я буду использовать его в качестве последнего средства.
4. @AnnaNoie AVX всегда использует IEEE-754, поэтому другого лучшего способа нет. Поскольку вы уже используете встроенные функции, нет никаких причин писать переносимый код, не зависящий от формата
5. Если вы храните целые байты, можете ли вы сделать их 0 / -1, чтобы вы могли расширить знак до 32-разрядного с
_mm256_cvtepi8_epi32
помощью , и И вместо shift ? Для этого потребуется отдельная векторная константа, иvpand
это только лучше, чемvpslld ymm, ymm, 31
если бы вы делали это в цикле, смешанном с математическими операциями FP, которые конкурировали бы за порты со сдвигом.
Ответ №1:
Поблагодарите всех за подсказки. Я придумал это решение, используя SSE4.1. Любое лучшее решение будет оценено.
const int size4 = (size / 4) * 4;
for (int i = 0; i < size4; i = 4) {
__m128i xmm1 = _mm_cvtepu8_epi32((__m128i) _mm_loadu_ps((float *) amp;bitchar[i]));
__m128 xmm2 = (__m128) _mm_slli_epi32(xmm1, 31);
__m128 xmm3 = _mm_xor_ps(xmm2, _mm_loadu_ps(amp;in[i]));
_mm_storeu_ps(amp;out[i], xmm3);
}
for (int i = size4; i < size; i ) {
out[i] = in[i] * (1 - 2 * bitchar[i]);
}
Комментарии:
1. Обычно вы бы использовали
_mm_loadu_si128((const __m128i *) amp;bitchar[i])
. (Или на самом деле встроенный для загрузки 4 или 8 байт в __m128i, а не 16 байт, но компиляторы часто отстой при сворачивании таких нагрузок в источник памяти дляvpmovzxbd
), И вы могли бы использовать_mm256_cvtepu8_epi32
для преобразования для распаковки в 256-битный вектор, если у вас есть AVX2, а также AVX.2. @PeterCordes спасибо. Я переключусь на
_mm_loadu_si128
. И на моей целевой машине нет AVX2.