Я ищу наиболее эффективный способ вычисления всех первых целых чисел с плавающей запятой X
внутри регистра SSE-128/AVX-256/AVX-512 (128, 256 и 512 бит), например. для float AVX1-256 я хочу получить в регистре X^1, X^2, X^3, X^4, X^5, X^6, X^7, X^8
данные X
на входе. Мне нужен код как для float-32, так и для double-64.
Я реализовал этот код для случая AVX-256/float-32:
__m256 _mm256_powers_ps(float x) {
float const x2 = x * x, x4 = x2 * x2;
return _mm256_mul_ps(
_mm256_mul_ps(
_mm256_setr_ps( x, x2, x, x2, x, x2, x, x2),
_mm256_setr_ps( 1, 1, x2, x2, 1, 1, x2, x2)
),
_mm256_setr_ps( 1, 1, 1, 1, x4, x4, x4, x4)
);
}
Я разработал его в основном, чтобы выглядеть просто. Но я думаю, что с точки зрения производительности это можно сделать быстрее, может быть, на одно или два умножения меньше. Кто-нибудь может предложить более быструю версию? Может быть, есть даже какая-то единая инструкция AVX для вычисления этих мощностей?
Меня интересуют версии с плавающей запятой 32 и двойной 64 для всех 128-битных (4 числа с плавающей запятой или 2 двойных), 256-битных (8 чисел с плавающей запятой или 4 двойных) и 512-битных SIMD-регистров (16 чисел с плавающей запятой или 8 двойных значений). ).
Я также реализовал, возможно, более быструю версию, выглядящую более сложной, но в которой на одно умножение меньше, сравнение скорости не проводилось:
__m256 _mm256_powers_ps(float x) {
float const x2 = x * x;
__m256 const r = _mm256_mul_ps(
_mm256_setr_ps( x, x2, x, x2, x, x2, x, x2),
_mm256_setr_ps( 1, 1, x2, x2, 1, 1, x2, x2)
);
return _mm256_mul_ps(r,
_mm256_setr_m128(_mm_set1_ps(1),
_mm_permute_ps(_mm256_castps256_ps128(r), 0b11'11'11'11))
);
}
Я также думал о том, что простое решение без SIMD может быть также быстрым из-за хорошей параллельной конвейерной обработки многих независимых умножений:
__m256 _mm256_powers_ps(float x) {
auto const x2 = x * x;
auto const x3 = x2 * x;
auto const x4 = x2 * x2;
return _mm256_setr_ps(
x, x2, x3, x4, x4 * x, x4 * x2, x4 * x3, x4 * x4);
}
Примечание: эти степени X
необходимы для этапа вычисления полинома с плавающей запятой или двойной точности, см. мой другой вопрос относительно вычисления полиномов на SIMD. Этот многочлен может иметь разную степень, иногда 3, иногда 6, иногда 9, иногда 15, даже 25 или 32 или 43 степени. Эти числа произвольны, на самом деле может быть использован целый диапазон полиномов от 1 до 48 степеней. Коэффициенты многочленов заранее заданы как константы. Но значение X
, для которого его надо вычислить, заранее неизвестно. Конечно, я буду использовать модуль FMA
для вычисления самого значения полигонов, но для вычисления полигонов с использованием SIMD необходимы предварительно вычисленные мощности X
.
Чаще всего будет использоваться целевой ЦП: Intel Xeon Gold 6230 с AVX-512, поэтому мне нужно оптимизировать код для него.
X
. Не ожидайте большого выигрыша от использования SSE/AVX для вашей спецификации. - person j6t   schedule 13.06.2021x
, с каким окружающим кодом это должно перекрываться? Является ли он более чувствительным к задержке этой операции, или к стоимости пропускной способности внешнего интерфейса, или к нагрузке внутреннего порта на любом заданном порту? Как сказал @j6t, если у вас есть более одного значенияx
для этого, вычисляйте векторы с одной и той же операцией для всего вектора. При необходимости транспонируйте весь набор векторов 8x8 в конце, если вам нужно сохранитьx1, x2, x3, ...
в память в таком порядке, чтобы не тратить свои тасовки впустую. - person Peter Cordes   schedule 13.06.2021_mm256_setr_ps
— он может плохо работать и не понимать, что это просто 64-битная трансляцияx, x2
, которую он мог бы сделать сvmovlhdup xmm0, xmm1
/vmulss xmm0, xmm0
/vbroadcastpD ymm0, xmm0
) - person Peter Cordes   schedule 13.06.2021X
. Этот вычислительный этап X Powers появляется в середине других SIMD-операций. - person Arty   schedule 13.06.2021double
. При необходимости его также можно передать как младшее слово 128-го или 256-го регистра. Результат нужен как 256-регистр. - person Arty   schedule 13.06.2021x
, не существует? В любом случае, вы могли бы так же легко иметьx
как младший элемент__m256
или__m128
. Или, если это еще не нижний элемент, вы можетеvpermps
транслировать его вместо простого перетасовывания в самый низ. Это упрощает оптимизацию перетасовки вместо использования скаляра_mm_set
. - person Peter Cordes   schedule 13.06.2021_mm_mul_ss
для[v]mulss
, позволяющая просто умножить младший скалярный элемент и оставить остальные без изменений; это похоже на прецедент для него. - person Peter Cordes   schedule 13.06.2021_mm256_setr_ps
, что, если сделать это наивно, может потребовать много перетасовок для каждого! Вы даже не пытаетесь сконструировать одно из другого, и я предполагаю, что только у clang есть много шансов заметить сходство между ними и создать одно из другого путем перетасовки или смешивания вместо перезапуска со скалярных входных данных. Ты про асм? Если да, то с какими компиляторами вы заботитесь об эффективной компиляции? (На самом деле в вашей ссылке Godbolt даже gcc не так плох, как я думал, но, конечно, не так хорош.) - person Peter Cordes   schedule 13.06.2021main
в Godbolt; это просто беспорядок. - person Peter Cordes   schedule 13.06.2021IACA or LLVM-MCA
и не знаю, как их использовать. Но было бы здорово научиться! - person Arty   schedule 13.06.2021_mm_setr_ps
. Посмотрите на все эти наивныеvinsertps
на godbolt.org/z/bj5rK9jfP. Re: IACA и LLVM-MCA: см. (Как) я могу предсказать время выполнения фрагмента кода с помощью анализатора машинного кода LLVM? и Что такое IACA и как его использовать?. и погуглите их. Godbolt может добавить представление анализа llvm-mca к выходным данным компилятора. - person Peter Cordes   schedule 13.06.2021-link there from which you can see that CLang created quite simple and stragihtforward code of single-double multiplications using
vmulss». Таким образом, он не выполнял никаких комбинированных инструкций SSE/AVX. - person Arty   schedule 13.06.2021vinsertps
инструкции для реализации_mm256_setr_ps
(godbolt.org/z/fxxvsnncx) не могут работать параллельно друг с другом из-за конфликтов ресурсов (ограниченная внутренняя пропускная способность), поэтому минимальная задержка не так хорошо, как можно было бы надеяться. Тем не менее, он избегает объединения в цепочку любых умножений, и при задержке 4c они намного медленнее, чем перемешивание 1c. Стоит учесть. - person Peter Cordes   schedule 13.06.2021x4 = x2*x2
необходимость ждать, пока x2 будет готов. Таким образом, перекрытие задержек умножения и перемешивания, вероятно, лучше, если делать это осторожно. - person Peter Cordes   schedule 13.06.2021x
на__m128
без использования каких-либо дополнительных инструкций по перемешиванию? например если это сумма массива, выполните hsum с перетасовкой, которая оставляет результат везде, т.е. высокая ‹-> низкая вместо высокой-> низкая перетасовка. Или это естественно внизу вектора без лишних перетасовок? (Или это скажем 2-й элемент, а остальные держат фигню?) - person Peter Cordes   schedule 13.06.2021mulss
это сделало быunpcklps
более мощным для создания вектора, такого как x, x2, x,x2.shufps
требует, чтобы оба элемента каждой 64-битной половины исходили из одного и того же источника, поэтому его трудно использовать сx
иx2
в нижней части разных векторов.vpermps
имеет только 1 вход. иunpcklpd
/movlhps
не очень помогают.) - person Peter Cordes   schedule 13.06.2021mulss
. Я мог бы написать это как ответ когда-нибудь. - person Peter Cordes   schedule 13.06.2021vpermt2ps
может спасти перетасовку, а маскирование слиянием для умножения и перетасовки может быть очень полезным. Можно ли с пользой повторно использовать константы векторов и масок, или между этими шагами нужно проделать много работы? (Я предполагаю, что может быть стоит перезагрузить константу, если она экономит задержку критического пути, если она не кэширует промахов больше, чем сохраняет. Настройка констант, конечно, всегда вне критического пути.) Или вам это нужно для актуальное наследие-SSE и AVX1? Для устаревшего SSE дополнительные копии регистров movaps могут иметь значение. - person Peter Cordes   schedule 13.06.2021K <= 6
быстрее использовать 128-битные вычисления даже на машине AVX-512, поэтому мне нужно использовать 25% всего регистра AVX-512, чтобы увеличить скорость. Для другого порога6 < K <= 13
мне нужно использовать 256 бит даже на машине AVX-512. А для13 < K
могу использовать полный AVX-512.K
здесь просто некий воображаемый параметр, контролирующий порог принятия решения о выборе 128/256/512, а не количество поплавков. Таким образом, 512 не всегда является самым быстрым способом вычисления конечной задачи. Не задача сил, а полная задача моей функции. - person Arty   schedule 13.06.2021insertps
, и до сих пор мои идеи для AVX1 в основном использовали вещи, которые будут работать в SSE2 (нет необходимости расширяться до 256 на ранней стадии, когда это было бы избыточно и нуждалось бы в связке1.0
), и строит результат __m256 из __m128. Однако я не пытался минимизировать в немmovaps
инструкций. Но я ожидаю, чтоdouble
будет существенно отличаться отfloat
, вероятно, намного проще. - person Peter Cordes   schedule 13.06.2021X
необходимы для этапа вычисления полинома с плавающей запятой или двойной точности. Этот полином может иметь разные степени, иногда 3, иногда 6, иногда 9, иногда 15, даже 25 или 32 степени. Эти числа произвольны, на самом деле может быть использован целый диапазон полиномов от 1 до 32 степеней. Коэффициенты многочленов заранее заданы как константы. Но значениеX
, для которого его надо вычислить, заранее неизвестно. Конечно, я буду использовать модуль FMA для вычисления самого полигона, но нужны и мощности. - person Arty   schedule 13.06.2021X^1, ..., X^8
, то впоследствии мне нужно как-то разделить их на два регистра, первый регистр должен содержать1, X^1, ..., X^7
(примечание 1 на первом месте), второй регистр должен содержатьX^8, X^8, ... X^8
(значение X^8 передается в эфир). Так что если вы знаете, как конвертировать в такие два регистра, пожалуйста, скажите. - person Arty   schedule 13.06.20211.0, x, x2, x3, ...
, которое вам нужно, в первую очередь, вместоx1 .. x8
? Вычисление set1(x^8) вместе с этой последовательностью, вероятно, так же просто. Но если вы усложнили себе задачу, используйте AVX512Fvalignd
для перехода на 1.0 и используйтеvpermps
для трансляции верхнего элемента. - person Peter Cordes   schedule 13.06.2021_mm256_fmadd_ss()
). Но если мой полигон имеет степень вроде 48, то гораздо быстрее предварительно вычислить степениX^1... X^8
, а затем выполнить FMA с 8 или 16 числами с плавающей запятой за раз. Ваш связанный источник оптимален для степеней ниже 6. Начиная со степени около 8, SSE2 уже помогает. И с 16 степени помогает AVX1, с 32 - AVX512. И у меня много 32-48-градусных полигонов. - person Arty   schedule 13.06.2021x
, используя SIMD для того, в чем он хорош (вертикальные операции, выполнение одного и того же действия с несколькими значениями вместо горизонтальных). Если вы не можете этого сделать, может быть еще эффективнее включать коэффициенты в какой-то момент по ходу дела. Или какая-то гибридная стратегия, включающая несколько разных коэффициентов одного и того же полинома в одном векторе, но также, возможно, с использованием FMA. например может быть, запустить четные и нечетные степени или просто силы из 1..4, чтобы потом меньше перетасовывать в hsum. - person Peter Cordes   schedule 13.06.2021x
, чтобы получить правильное конечное значение. Тогда у вас есть сумма из 4 элементов, которая требует 2 перетасовки и 2 добавлений. - person Peter Cordes   schedule 14.06.20212x
ускорение на больших полигонах вроде 30 градусов, а на более мелких полигонах вроде 8-10 градусов даже на них дает1.4-1.5x
ускорение. AVX1 дает3.5x
ускорение на больших полигонах (30 градусов) и около1.5x
на меньших 8,10 - person Arty   schedule 14.06.2021_mm_mul_ps()
или_mm256_mul_ps()
или_mm512_mul_ps()
, то все они займут одинаковое количество циклов на одном процессоре? - person Arty   schedule 14.06.2021vmulps xmm
(и FMA) конкурируют за те же порты выполнения, что иvmulps ymm
иzmm
. (Хотя с 512-битным ZMM есть забавная штука, отключение порта 1, но есть отдельный 512-битный блок FMA на порту 5.) - person Peter Cordes   schedule 14.06.2021_mm256_mul_ps()
для отлитой нижней части вместо использования_mm512_mul_ps()
? То же самое для ФМА? - person Arty   schedule 14.06.2021-ffast-math
для этого? Я заметил, что CLang иногда объединяет в регистры SSE2-128 две двойные операции. Но я никогда не замечал, что он объединяется в операции AVX1-256 (4 двойных), даже если установлен флаг-mavx
. Есть ли способ предложить CLang объединиться в AVX1-256 и AVX-512 с помощью какого-то специального флага командной строки? - person Arty   schedule 14.06.2021-march=native
, а не только-mavx
, чтобы сообщить clang, для какой машины он настраивается, а не только для того, какие расширения ISA доступны. Но обратите внимание, что перетасовки с пересечением дорожек имеют более высокую задержку (3c против 1c), поэтому реже стоит делать слишком много перетасовки вместе. Очевидно, что в цикле или что-то в этом роде он будет автоматически векторизовать массив с широкими векторами (вплоть до ширины предпочтительного вектора по умолчанию, которая часто равна 256 даже в системах с поддержкой AVX-512). -ffast-math иногда может помочь даже в тех случаях, когда оптимизация кажется вам законной без, особенно. для ГЦК. - person Peter Cordes   schedule 14.06.2021std::tuple<__m256, __m256, __m256>
, или, может быть, лучше иметь один возврат__m256
плюс два указателя__m256 * ret
в качестве параметров функции? Или даже ссылки типа__m256 & ret
в качестве параметров? В случае, если это встроенная функция. Или решения для кортежа, указателя и эталона одинаковы для оптимизации на стороне компилятора? Также, если не встроенная функция, что лучше? - person Arty   schedule 14.06.2021_mm_hadd_ps()
два раза, а затем_mm_cvtss_f32()
? Или, выполнив_mm_store_ps()
для некоторого выровненного по tmp массиваa
из 4 чисел с плавающей запятой, а затем вычисливa[0] + a[1] + a[2] + a[3]
? Какой способ быстрее? Какая может быть разница во времени цикла для этих двух способов? Как насчет того же вопроса для регистра 256 (сумма 8 поплавков)? - person Arty   schedule 14.06.2021_mm512_mul_ps()
avx512, сколько независимых таких операций можно выполнять параллельно на современном процессоре, например Skylake? Во встроенном руководстве Intel я вижу задержку 4 и пропускную способность 0,5 для Skylake. Что это на самом деле означает? Означает ли это, что если у меня есть 4 смежные независимые инструкции_mm512_mul_ps()
, то все 4 будут работать параллельно? Так что 1-я инструкция завершится на такте 4, 2-я на такте 4,5, 3-я на такте 5, 4-я на такте 5,5? Как насчет 8 инструкций? Какое оптимальное количество? 2 или 4 или 8 мул инструкции? - person Arty   schedule 14.06.2021mul
или, может быть,fmadd
и говорю, какой у меня процессор, например Skylake. Затем калькулятор подсказывает, сколько оптимальных независимых инструкций такого типа я должен разместить рядом друг с другом, чтобы насытить весь умножающий модуль? Мне нужно знать это и для старых процессоров, а также для AVX1-256 и SSE2-128, а также для float-32 и double-64. - person Arty   schedule 14.06.2021