SIMD и разница между упакованной и скалярной двойной точностью

Я читаю руководство Intel по встроенным функциям при реализации поддержки SIMD. У меня есть несколько затруднений, и мои вопросы приведены ниже.

  1. __m128 _mm_cmpeq_ps (__m128 a, __m128 b) в документации говорится, что он используется для сравнения упакованных чисел с плавающей запятой одинарной точности. Что значит "упаковано"? Нужно ли мне как-то упаковать свои значения с плавающей запятой, прежде чем я смогу их использовать?

  2. Для двойной точности есть встроенные функции вроде _mm_cmpeq_sd, что означает сравнение «нижних» элементов с плавающей запятой двойной точности. Что означают нижние и верхние элементы двойной точности? Могу ли я использовать их для сравнения вектора элементов типа C ++ double или нет? Или мне нужно как-то их обработать, прежде чем сравнивать?


person user1461001    schedule 25.04.2013    source источник


Ответы (2)


В SSE 128-битные регистры могут быть представлены как 4 элемента по 32 бита или 2 элемента по 64 бита.

SSE определяет два типа операций; скалярный и упакованный. Скалярная операция работает только с наименее значимым элементом данных (биты 0 ~ 31 или 0 ~ 63), а пакетная операция вычисляет все элементы параллельно.

_mm_cmpeq_sd предназначен для работы с двойной точностью (64 -bit) элементов с плавающей запятой и будет сравнивать только наименее значимый элемент данных (первые 64 бита) из двух операндов (скаляр).

_mm_cmpeq_pd предназначен для работы с двойной точностью (64 -bit) элементы с плавающей запятой, но будут сравнивать каждые две группы по 64 бита параллельно (упакованные).

_mm_cmpeq_ss предназначен для работы с одинарной точностью (32 -bit) элементов с плавающей запятой и будет сравнивать только наименее значимый элемент данных (первые 32 бита) из двух операндов (скаляр).

_mm_cmpeq_ps предназначен для работы с одинарной точностью (32 -bit) элементов с плавающей запятой и будет сравнивать каждую группу из 32 бит параллельно (упаковано).

Если вы используете 32-битное число с плавающей запятой, вы можете упаковать его в квадруплет, чтобы использовать 128-битное пространство. Таким образом, _mm_cmpeq_ps сможет провести 4 сравнения параллельно.

Если вы используете 64-битное двойное, вы можете упаковать его в пару, чтобы использовать 128-битное пространство. Таким образом, _mm_cmpeq_pd сможет провести 2 сравнения параллельно.

Если вы хотите проводить только одно сравнение за раз, вы можете использовать _mm_cmpeq_sd для сравнения двух 64-битных чисел двойной точности или _mm_cmpeq_ss для сравнения двух 32-битных чисел с плавающей запятой.

Обратите внимание, что _mm_cmpeq_sd и _mm_cmpeq_pd - это SSE2, а _mm_cmpeq_ss и _mm_cmpeq_ps - SSE.

person zakinster    schedule 25.04.2013
comment
Этот ответ, по сути, нормальный, за исключением того, что _mm_cmpeq_sd будет сравнивать только наименее значимый элемент данных (первые 32 бита). _mm_cmpeq_sd предназначен для работы с doubles (отсюда и буква d в имени команды), поэтому требуется исправление: _mm_cmpeq_sd будет сравнивать только наименее значимый элемент данных (первые 64 бита). Аналогичное недоразумение - это следующий абзац (в регистр длиной 128 бит могут поместиться только 2 двойных значения, а имя функции должно заканчиваться на букву d). - person zkoza; 12.01.2021
comment
@zkoza да, между операциями double и float произошла путаница, спасибо, что указали на это. Я исправил это в последнем редактировании и добавил все четыре операции скалярного / упакованного и одинарного / двойного, чтобы избежать путаницы. - person zakinster; 13.01.2021

В этом контексте «упакованный» означает «несколько одинаковых типов, помещенных в один кусок», поэтому «упакованные числа с плавающей запятой одинарной точности» означают 4 * 32-битные числа с плавающей запятой, сохраненные как 128-битное значение.

Вам нужно либо «упаковать» каждое значение в регистр с помощью различных PACK* инструкций, либо данные уже «упакованы» в памяти, например массив из (кратных) 4 значений с плавающей запятой [, которые соответствующим образом выровнены].

Скаляр означает «одно значение» в младших n битах регистра (например, double будет младшими 64 битами 128-битного регистра SSE).

person Mats Petersson    schedule 25.04.2013
comment
Если у вас есть несколько скалярных чисел с плавающей точкой в ​​регистрах XMM для перемешивания в один регистр, вы действительно хотите использовать перемешивание unpcklps. pack инструкции, такие как packssdw, сужают целочисленные операции. (Таким образом, распаковка с нулем - это своего рода инверсия pack (расширение целочисленных элементов), и это может быть источником этого странного соглашения об именах. Помните, что Intel Integer SIMD (MMX) существовала до fp ps SSE1 и pd SSE2.) - person Peter Cordes; 13.01.2021