Почему инструкция vhaddps добавляет таким сложным образом? - PullRequest
2 голосов
/ 12 мая 2019

Инструкция vhaddps добавляет очень своеобразным образом:

enter image description here

Источник: https://www.felixcloutier.com/x86/haddps

В чем причина этого? Для каких случаев используется эта инструкция? Похоже, что дизайн имеет в виду что-то конкретное.

Ответы [ 2 ]

2 голосов
/ 12 мая 2019

Как и во многих инструкциях шириной 256 бит, верхние 128 бит vhaddps ymm ymm ymm - это просто копия 128-битной ширины vhaddps xmm xmm xmm инструкция. Следующий пример показывает, что имеет смысл определить vhaddps xmm xmm xmm таким сложным образом: дважды используя эту инструкцию дает горизонтальную сумму 4 xmm регистров.

/* gcc -m64 -O3 hadd_ex.c -march=sandybridge           */
#include<immintrin.h>
#include<stdio.h>
int main(){
    float tmp[4];
    __m128 a = _mm_set_ps(1.0, 2.0, 3.0, 4.0);
    __m128 b = _mm_set_ps(10.0, 20.0, 30.0, 40.0);
    __m128 c = _mm_set_ps(100.0, 200.0, 300.0, 400.0);
    __m128 d = _mm_set_ps(1000.0, 2000.0, 3000.0, 4000.0);
    __m128 sum1 = _mm_hadd_ps(a, b);
    __m128 sum2 = _mm_hadd_ps(c, d);
    __m128 sum = _mm_hadd_ps(sum1, sum2);
    _mm_storeu_ps(tmp,sum);
    printf("sum = %f  %f  %f  %f\n", tmp[0], tmp[1], tmp[2], tmp[3]);
    return 0;
}

Выход:

sum = 10.000000  100.000000  1000.000000  10000.000000
2 голосов
/ 12 мая 2019

Это 2-х линейные haddps инструкции в низком и верхнем 128-битных каналах. Большинство инструкций AVX на самом деле не расширяют операцию до 256-битной, они выполняют 2 отдельные операции внутри линии . Это делает AVX трудным в использовании, особенно без AVX2 для перестановок с пересечением полос с зернистостью менее 128 бит!

Но это экономит транзисторы против, например, создание vpshufb одного 32-байтового тасования вместо 2-х 16-байтовых тасов. AVX2 даже не обеспечивает, что: Где VPERMB в AVX2? (приходится ждать AVX512VBMI).

(связано: лучший способ перестановки по линиям AVX? Кроме того, AVX512 добавляет множество гибких перестановок, пересекающих полосы, но версии инструкций SSE / AVX AXV512, такие как vhaddps zmm, все еще находятся в полоса. См. также Дают ли 128-битные операции перекрестной полосы в AVX512 лучшую производительность? )

Для цепочки AVX2 vpack* обычно требуется vpermq, чтобы выполнить исправление при пересечении полосы движения в конце, если только вы не собираетесь снова распаковывать линию. Таким образом, в большинстве случаев 2-кратные тасовки в полосе хуже, чем полная операция с 256-битной шириной, но это не то, что мы получаем от AVX. Часто все еще требуется ускорение перехода на 256-битную вектор со 128, даже если для исправления поведения на линии требуются дополнительные тасовки, но это часто означает, что это не 2-кратное ускорение, даже если нет узких мест в памяти.

vpalignr, вероятно, является наиболее вопиющим примером того, что 2x 128-битные версии одного и того же шаффла сами по себе не являются полезным строительным блоком; Я не могу вспомнить, видел ли я когда-нибудь сценарий использования двух отдельных оконных байтовых данных. О, на самом деле да, если вы кормите его с помощью vperm2i128 Как эффективно объединить два вектора с помощью AVX2? (версия VPALIGNR с пересечением полосы движения) , но обычно невыровненные нагрузки лучше подходят для процессоров, поддерживающих AVX2.


Варианты использования для (v)haddps очень ограничены

Может быть, Intel планировала превратить haddps в инструкцию с одним занятием в какой-то момент после введения ее в SSE3, но этого не произошло.

Варианты использования включают в себя вещи типа транспонирования и добавления , в которых вам все равно придется перетасовывать оба входа для вертикального addps. например Наиболее эффективный способ получения __m256 горизонтальных сумм из 8 исходных __m256 векторов включает vhaddps. (Плюс AVX1 vperm2f128 для исправления поведения на линии.)

Многие люди ошибочно полагают, что это хорошо для горизонтальных сумм одного вектора, но и 128, и 256-битные (v)haddps декодируют до 2x случайных чисел, чтобы подготовить входные векторы для вертикального (v)addps упа. Для горизонтальной суммы вам понадобится только 1 случайный шаг на добавление. ( Самый быстрый способ сделать горизонтальную векторную сумму с плавающей запятой на x86 )

Сначала сужение до 128-битного (с vextractf128 / vaddps) обычно является лучшим первым шагом, если вы не хотите, чтобы результат транслировался на каждый элемент, и вы не находитесь на процессоре AMD (где 256-битный вектор операции расшифровываются как минимум до 2 моп или более для перестановок, пересекающих полосу движения). (v)haddps xmm или целое число vphaddd полезно для горизонтальных сумм, если вы оптимизируете размер кода, а не скорость, например мой ответ с машинным кодом x86 на вопрос по коду для гольфа "Вычислите среднее значение двух чисел".

Неразрушающие операнды назначения AVX также устраняют некоторые преимущества наличия многопользовательской инструкции. Без AVX иногда вы не можете избежать movaps для копирования регистра перед его уничтожением, поэтому выпечка 2x shuffle + add to 1 фактически спасла мопс от необходимости делать это вручную с movaps + shufps.

...