Распаковка полубайтов в байты – Прямые инструкции/Эффективный способ реализации и сохранения знака

const __m128i mask = _mm_set1_epi8(0x0F);
const __m128i vec_unpack_one = _mm_and_si128(vec, mask);
const __m128i vec_unpack_two = _mm_and_si128(_mm_srli_epi16(vec, 4), mask);

У меня есть набор из 32 полубайтов, хранящихся в vec. Я хочу распаковать его и сохранить каждый полубайт как байт, что и пытаются сделать 2-я и 3-я строки фрагмента кода. Однако я хочу сохранить знак полубайта и расширить его до байта.

Например, один из 8-битных элементов vec — 01111011.

В vec_unpack_one оно распаковывается в настоящее время как 00001011, тогда как в vec_unpack_two оно распаковывается как 00000111. Однако я хочу, чтобы распакованное значение в vec_unpacked_one было 11111011, иначе значения, используемые в последующей операции, будут отличаться от того, что было на самом деле задумано.

Текущее решение, которое я имел в виду, состоит в том, чтобы отделить старший бит полубайта от побитовых операций и выполнить какие-то маскируемые операции или операции, основанные на битах. Но есть ли способы добиться этого посредством прямого указания или более эффективными способами. Предложения приветствуются. Спасибо

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
126
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Расширение знака полубайта можно выполнить с помощью pshufb (_mm_shuffle_epi8) в качестве таблицы поиска. Вам все равно необходимо замаскировать старшие биты, поскольку установка старшего бита в индексном байте обнуляет соответствующий вывод вместо индексации другого вектора.

Таким образом, вы все равно начнете с того же кода, что у вас есть (стандартный способ разделения полубайтов), и сделаете
v0 = _mm_shuffle_epi8(sign_extend_lut, v0) и то же самое для v1.


Вероятно, это ваш лучший выбор по сравнению с битхаком типа (x ^ m) - m (2 инструкции на половину с использованием _mm_xor_si128 и _mm_sub_epi8), где m - это 1U << 3 или 8 (Знак расширения девятибитного числа в C / https://graphics.stanford .edu/~seander/bithacks.html#FixedSignExtend), для которого также необходимо заранее обнулить старшие биты.

Если ваш окружающий код не слишком перегружен перемешиванием, особенно если важны старые процессоры Intel (семейство Haswell и Skylake с пропускной способностью перемешивания всего 1/такт: https://uops.info). Тогда, возможно, рассмотрите битхак.

Мы можем выполнить XOR входных данных с помощью _mm_set1_epi8(0x88) перед разделением полубайтов, так что эта версия битхака будет всего на один моп дороже, чем версия pshufb вместо двух, хотя тогда для нее потребуются две разные векторные константы. (Спасибо @chtz).


Самый узкий сдвиг SIMD в x86 — 16-битный, поэтому арифметический сдвиг вправо, к сожалению, не поможет.

Если бы вы позже расширили до 16 или 32-бит, вы могли бы vpmovsxbw или vpmovsxbd, а затем арифметический сдвиг вправо. (_mm_cvtepi8_epi16 / _mm_srai_epi16 или их _mm256 эквиваленты), по крайней мере, для верхних полубайтов.

Для битхака вы можете выполнить xor каждого байта с помощью 0x88 перед сдвигом + маскированием, что приведет к увеличению всего на один моп по сравнению с версией pshufb. А вычитание 0x08 (или добавление 0xf8) в некоторых случаях может сопровождаться последующими инструкциями.

chtz 24.06.2024 10:10

Другие вопросы по теме