Умножение байтов для получения 16-битных данных без смещения - PullRequest
0 голосов
/ 11 января 2019

Все еще изучая искусство SIMD, у меня есть вопрос: у меня есть два упакованных 8-битных регистра, которые я хотел бы умножить на _mm_maddubs_epi16 (pmaddubsw), чтобы получить 16-битный упакованный регистр.

Я знаю, что эти байты будут выдавать всегда число меньше 256, поэтому я бы хотел не тратить оставшиеся 8 бит. Например, результат _mm_maddubs_epi16(v1, v2) должен записать результат в r, где XX, а не там, где он будет (обозначен __).

v1  (04, 00, 0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01, 00)
v2  (04, 00, 0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01, 00)

r   (__, XX, __, XX, __, XX, __, XX, __, XX, __, XX, __, XX, __, XX)

Могу ли я сделать это без смещения результата?

PS . У меня нет хорошего процессора, я ограничен инструкциями AVX.

Ответы [ 2 ]

0 голосов
/ 12 января 2019

На вашей векторной диаграмме самый верхний элемент слева или справа? Расположены ли XX в старшем или младшем байте результата pmaddubsw?

Чтобы получить результаты в младшем байте слова из входных данных в старшем байте каждого слова:

Используйте _mm_mulhi_epu16, чтобы эффективно выполнять (v1 << 8) * (v2 << 8) >> 16, получая результат в байте, противоположном входным словам. Поскольку вы говорите, что произведение строго меньше 256, вы получите 8-битный результат в младшем байте каждого 16-битного слова.

(Если ваши входы подписаны, используйте _mm_mulhi_epi16, но тогда отрицательный результат будет расширен до 16 бит.)

Чтобы получить результаты в старшем байте слова из входных данных в младшем байте

Вам нужно изменить способ загрузки / создания одного из входов, чтобы вместо

         MSB LSB | MSB LSB
v1_lo   (00, 04,   00, 0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01)
 element# 15 14   13   12 ...                                           0

у вас есть это: (оба используют нотацию Intel, где левый элемент является наибольшим числом, поэтому вектор сдвигается как _mm_slli_epi128 смещение байтов влево на диаграмме).

         MSB LSB | MSB LSB 
v1_hi   (04, 00,   0e, 00, 04, 00, 04, 00, 0a, 00, 0f, 00, 05, 00, 01, 00)
 element# 15 14   13   12 ...                                           0

Если v2 все еще имеет ненулевые байты в верхней половине каждого элемента слова, просто _mm_mullo_epi16(v1_hi, v2), и вы получите (v1 * v2) << 8 бесплатно.

Если вы уже распаковываете байты с нулями для получения v1 и v2, распакуйте другой способ . Если вы использовали pmovzx (_mm_cvtepu8_epi16), то переключитесь на _mm_unpacklo_epi8(_mm_setzero_si128(), packed_v1 ).

Если вы загружали эти векторы из памяти в этой форме, уже заполненной нулями, используйте смещение нагрузки без выравнивания на 1 байт, чтобы нули оказались в противоположном месте.


Если вы действительно хотите начать с входных байтов, которые не распакованы с нулями, я не думаю, что вы можете избежать этого. Или, если вы маскируете вместо распаковки (чтобы сохранить пропускную способность shuffle-port, используя вместо этого _mm_and_si128), вам, вероятно, понадобится смещение куда-нибудь. Вы можете сдвинуть вместо маскирования в одну сторону, однако, используя v1_hi = _mm_slli_epi16(v, 8): сдвиг влево на 8 с гранулярностью слова выбьет оставивший младший байт обнуленным.

0 голосов
/ 12 января 2019

Shift v1 или v2 и затем используйте _mm_mullo_epi16().

Возможная проблема XY? Я предполагаю, что _mm_unpacklo_epi8() и _mm_packus_epi16() могут быть полезны для вас.

...