В чем разница между логическими внутренними функциями SSE?

StackOverflow https://stackoverflow.com/questions/2804902

Вопрос

Есть ли какая-либо разница между логическими встроенными функциями SSE для разных типов?Например, если мы возьмем операцию ИЛИ, есть три встроенных функции:_mm_or_ps, _mm_or_pd и _mm_or_si128 делают одно и то же:вычислять побитовый ИЛИ своих операндов.Мои вопросы:

  1. Есть ли разница между использованием той или иной встроенной функции (с соответствующим приведением типов).Не будет ли каких-либо скрытых затрат, таких как более длительное выполнение в какой-то конкретной ситуации?

  2. Эти встроенные функции соответствуют трем различным инструкциям x86 (por, orps, orpd).Есть ли у кого-нибудь идеи, почему Intel тратит драгоценное пространство кода операции на несколько инструкций, которые делают одно и то же?

Это было полезно?

Решение

Я думаю, что все три эффективно одинаковы, то есть 128 бит побитовые операции. Причина, по которой существуют разные формы, вероятно, исторические, но я не уверен. Я думаю, это возможно Что может быть некоторое дополнительное поведение в версиях плавающих точек, например, когда есть назы, но это чистые догадки. Для нормальных входов инструкции кажутся взаимозаменяемыми, например,

#include <stdio.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include <xmmintrin.h>

int main(void)
{
    __m128i a = _mm_set1_epi32(1);
    __m128i b = _mm_set1_epi32(2);
    __m128i c = _mm_or_si128(a, b);

    __m128 x = _mm_set1_ps(1.25f);
    __m128 y = _mm_set1_ps(1.5f);
    __m128 z = _mm_or_ps(x, y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    c = (__m128i)_mm_or_ps((__m128)a, (__m128)b);
    z = (__m128)_mm_or_si128((__m128i)x, (__m128i)y);

    printf("a = %vld, b = %vld, c = %vld\n", a, b, c);
    printf("x = %vf, y = %vf, z = %vf\n", x, y, z);

    return 0;
}

$ gcc -Wall -msse3 por.c -o por

$ ./por

a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000
a = 1 1 1 1, b = 2 2 2 2, c = 3 3 3 3
x = 1.250000 1.250000 1.250000 1.250000, y = 1.500000 1.500000 1.500000 1.500000, z = 1.750000 1.750000 1.750000 1.750000

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

  1. Есть ли разница между использованием той или иной встроенной функции (с соответствующим приведением типов).Не будет ли каких-либо скрытых затрат, таких как более длительное выполнение в какой-то конкретной ситуации?

Да, могут быть причины, по которым стоит выбрать один вариант, а не другой.другой.

1: Иногда возникает один или два дополнительных цикла задержки (задержка пересылки), если выход целочисленного исполнительного блока необходимо направить на вход исполнительного блока FP или наоборот.Для перемещения 128 бит данных в любое из многих возможных мест назначения требуется МНОГО проводов, поэтому разработчикам ЦП приходится идти на компромиссы, например, иметь прямой путь от каждого выхода FP к каждому входу FP, а не ко ВСЕМ возможным входам.

Видеть этот ответ, или Документ по микроархитектуре Агнера Фога для обходных задержек.Найдите «Задержки обхода данных на Nehalem» в документе Агнера;там есть несколько хороших практических примеров и дискуссий.У него есть раздел для каждой микроарки, которую он проанализировал.

Тем не менее, задержки для передачи данных между различными доменами или различными типами регистров меньше на песчаном мосту и мосту Айви, чем на Нехалеме, и часто нулевой.- Микроарк Агнер Фог Док

Помните, что задержка не имеет значения, если она не находится на критическом пути вашего кода.С использованием pshufd вместо movaps + shufps может быть победой, если вашим узким местом является пропускная способность, а не задержка вашего критического пути.

2: А ...ps версия занимает на 1 байт кода меньше, чем две другие.Это приведет к другому выравниванию следующих инструкций, что может иметь значение для декодеров и/или строк кэша uop.

3: Последние процессоры Intel могут запускать версии FP только на порту 5.

  • Мером (Core2) и Пенрин: orps может работать на p0/p1/p5, но только в целочисленном домене.Предположительно, все три версии декодируются в один и тот же uop.Таким образом, происходит задержка междоменной пересылки.(ЦП AMD тоже делают это:Побитовые инструкции FP выполняются в домене iec.)

  • Нехалем / Сэндибридж / IvB / Хасуэлл / Бродвелл: por может работать на p0/p1/p5, но orps может работать только на порту 5.p5 также необходим для тасования, но блоки FMA, FP add и FP mul находятся на портах 0/1.

  • Скайлейк: por и orps оба имеют пропускную способность 3 за цикл.Информации о задержках пересылки пока нет.

Обратите внимание, что в SnB/IvB (AVX, но не AVX2) только p5 должен обрабатывать 256b логических операций, поскольку vpor ymm, ymm требуется AVX2.Вероятно, это не было причиной изменения, поскольку это сделал Нехалем.

Как выбирать с умом:

Если пропускная способность логической операции на порту 5 может быть узким местом, используйте целочисленные версии, даже для данных FP.Это особенно актуально, если вы хотите использовать целочисленные перемешивания или другие инструкции по перемещению данных.

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

Если вы просто хотите установить/очистить/перевернуть бит в векторах FP между инструкциями FP add и mul, используйте команду ...ps логику, даже для данных двойной точности, поскольку одинарный и двойной FP являются одним и тем же доменом на каждом существующем процессоре, и ...ps версии короче на один байт.

Существуют практические/человеческие причины для использования ...pd версии, однако это часто перевешивает экономию 1 байта кода.Читабельность вашего кода другими людьми является фактором:Они будут задаваться вопросом, почему вы рассматриваете свои данные как одиночные, хотя на самом деле их число удвоилось.Особеннос внутренними функциями C/C++, засоряя ваш код приведениями между __mm256 и __mm256d не стоит того.Если настройка на уровне выравнивания insn имеет значение, пишите напрямую на ассемблере, а не на интринсах!(Увеличение инструкции на один байт может улучшить ситуацию с плотностью строк кэша и/или декодерами.)

Для целочисленных данных используйте целочисленные версии.Сохранение одного байта инструкции не стоит задержки обхода, а целочисленный код часто полностью занимает порт 5 перетасовкой.Для Haswell многие инструкции перетасовки/вставки/извлечения/упаковки/распаковки стали только p5 вместо p1/p5 для SnB/IvB.

  1. Эти встроенные функции соответствуют трем различным инструкциям x86 (por, orps, orpd).Есть ли у кого -нибудь идеи, почему Intel тратит впустую пространство для драгоценного кода для нескольких инструкций, которые делают то же самое?

Если вы посмотрите на историю этих наборов команд, вы сможете понять, как мы к этому пришли.

por  (MMX):     0F EB /r
orps (SSE):     0F 56 /r
orpd (SSE2): 66 0F 56 /r
por  (SSE2): 66 0F EB /r

MMX существовал до SSE, поэтому он выглядит как коды операций для SSE (...ps) инструкции были выбраны из одинаковых 0F xx космос.Тогда для SSE2 ...pd версия добавила 66 префикс размера операнда к ...ps код операции, а в целочисленной версии добавлен 66 префикс версии MMX.

Они мог не учел orpd и/или por, но они этого не сделали.Возможно, они думали, что будущие конструкции процессоров могут иметь более длинные пути пересылки между разными доменами, и поэтому использование инструкции сопоставления для ваших данных будет более сложной задачей.Несмотря на то, что существуют отдельные коды операций, AMD и ранние версии Intel рассматривали их одинаково, как int-vector.

Согласно рекомендациям по оптимизации Intel и AMD, смешивающие op Типы OP с типовыми типами данных, дают удар производительности в качестве внутреннего тегов CPU 64 бита половинки реестра для конкретного типа данных. Это, кажется, в основном в основном влияет на прокладку труб, поскольку инструкция декодируется, а UOPS запланированы. Функционально они дают тот же результат. Новые версии для целых типов данных имеют большую кодировку и забрать больше места в сегменте кода. Поэтому, если размер кода является проблемой, используйте старые OPS, так как они имеют меньшую кодировку.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top