Почему задержка команды sqrtsd изменяется в зависимости от ввода? Процессоры Intel - PullRequest
9 голосов
/ 12 марта 2020

Что ж, в Intel intrinsi c guide указано, что команда с именем "sqrtsd" имеет задержку 18 циклов.

Я протестировал его с моей собственной программой, и это правильно, если, например, мы принимаем 0,15 в качестве ввода. Но когда мы берем 256 (или любое 2 ^ x) число, тогда задержка составляет только 13. Почему это?

Одна теория, которая у меня была, состоит в том, что, поскольку 13 - это задержка "sqrtss", которая такая же, как "sqrtsd", но сделанная для 32-битных чисел с плавающей запятой, тогда, возможно, процессор был достаточно умен, чтобы понять, что 256 может уместиться в 32 bit и, следовательно, используйте эту версию, в то время как 0.15 требуется полная 64-битная версия, поскольку она не представляется конечным образом.

Я делаю это, используя встроенную сборку, вот соответствующая часть, скомпилированная с помощью g cc - O3 и -fno-tree-vectorize.

static double sqrtsd (double x) {
    double r;
    __asm__ ("sqrtsd %1, %0" : "=x" (r) : "x" (x));
    return r;
}

1 Ответ

10 голосов
/ 13 марта 2020

SQRT * и DIV * являются единственными двумя «простыми» инструкциями ALU (одиночные операции, без микрокодирования ветвления / зацикливания), которые имеют зависящую от данных пропускную способность или задержку на современных процессорах Intel / AMD. (Не считая ассистентов микрокода для ненормальных или ненормальных значений FP в add / multiply / fma). Все остальное в значительной степени исправлено, поэтому механизму неупорядоченного планирования не нужно ждать подтверждения того, что какой-то цикл был готов, он просто знает, что он будет.

Как обычно, встроенные функции Intel Руководство дает упрощенную картину производительности. Фактическая задержка не является фиксированными 18 циклами для двойной точности на Skylake. (Исходя из числа, которое вы выбрали для цитирования, я предполагаю, что у вас есть Skylake.)

div / sqrt сложно реализовать; даже в аппаратном обеспечении лучшее, что мы можем сделать, - это итеративный процесс уточнения. Уточнение большего количества битов одновременно (делитель radix-1024 начиная с Broadwell) ускоряет его (см. в этом разделе «Вопросы и ответы» об аппаратном обеспечении ). Но он все еще достаточно медленный, чтобы использовать ранний выход для ускорения простых случаев (Или, возможно, механизм ускорения просто пропускает шаг настройки для мантисс с нулем на современных процессорах с частично конвейерным div / sqrt У старых процессоров пропускная способность = задержка для FP div / sqr; этот исполнительный блок сложнее передать.)


https://www.uops.info/html-instr/VSQRTSD_XMM_XMM_XMM.html показывает, что Skylake SQRTSD может варьироваться от 13 до 19 задержка цикла Номера SKL (клиента) показывают только 13 циклов задержки, но мы можем видеть из подробной страницы SKL vsqrtsd , что они тестировали только с input = 0. Номера SKX (сервера) показывают 13-19 циклов задержки. ( Эта страница содержит подробную разбивку тестового кода, который они использовали, включая двоичные битовые комбинации для тестов.) Аналогичное тестирование (только с 0 для клиентских ядер) было выполнено на не VEX sqrtsd xmm, xmm с. : /

InstLatx64 Результаты показывают лучшие / худшие задержки 13–18 циклов на Skylake-X (который использует то же ядро, что и Skylake-клиент, но с включенным AVX512).

Таблицы инструкций Агнера Фога показывают задержку цикла 15-16 на Skylake. (Агнер обычно проводит тестирование с диапазоном различных входных значений.) Его тесты менее автоматизированы и иногда не совсем совпадают с другими результатами.

Что ускоряет некоторые случаи?

Обратите внимание, что большинство МСА (включая x86) используют двоичный с плавающей запятой :
биты представляют значения в виде линейного значения и (aka mantissa) раз 2 exp и знака бит.

Кажется, что на современном Intel может быть только 2 скорости (по крайней мере, начиная с Haswell) (см. обсуждение с @harold в комментариях.) Например, даже степени 2 все быстрые , как 0,25, 1, 4 и 16. Они имеют тривиальную мантиссу = 0x0, представляющую 1,0. https://www.h-schmidt.net/FloatConverter/IEEE754.html имеет хороший интерактивный десятичный <-> битовый преобразователь для одинарной точности с флажками для установленных битов и аннотаций того, что представляют мантисса и показатель степени.

На Skylake единственные быстрые случаи, которые я обнаружил при быстрой проверке, это даже мощностей 2 , таких как 4.0, но не 2.0. Эти числа имеют точный sqrt-результат, и на входе, и на выходе имеется мантисса 1,0 (только неявный набор из 1 бита). 9.0 не является быстрым, даже если он точно представим, как и результат 3.0. 3.0 имеет мантиссу = 1,5 только с самым старшим битом из набора мантиссы в двоичном представлении. 9,0 мантисса составляет 1,125 (0b00100 ...). Таким образом, ненулевые биты очень близки к вершине, но, по-видимому, этого достаточно для дисквалификации.

(+-Inf и NaN тоже быстрые. Так же как и обычные отрицательные числа: результат = -NaN . Я измеряю задержку 13 циклов для них на i7-6700k, так же, как для 4.0. Против задержки 18 циклов для медленного случая.)

x = sqrt(x) определенно быстро с x = 1.0 (все нулевые мантиссы за исключением неявного начального 1 бита). Он имеет простой ввод и простой вывод.

С 2. 0 вход также прост (мантисса с нулевым значением и показателем 1 выше), но выход не является круглым числом. sqrt (2) иррационально и, следовательно, имеет бесконечные ненулевые биты в любой базе. Это, по-видимому, замедляет работу Skylake.

Таблицы команд Агнера Фога говорят, что целочисленная div команда AMD AMD K10 зависит от количества значащих битов в дивиденде (входной), а не частное, но при поиске в pdf-таблицах и инструкциях по микроарху Агнера не было найдено ни сносок, ни информации о том, как конкретно sqrt зависит от данных. быть больше места для диапазона скоростей. Я думаю, количество значащих битов в мантиссе входа , вероятно, будет иметь значение. Меньшее количество значащих бит (больше конечных нулей в значении) делает это быстрее, если это правильно. Но опять же, на Haswell / Skylake единственными быстрыми случаями кажутся даже степени 2.


Вы можете проверить это с чем-то, что соединяет выход обратно с входом без прерывания зависимость данных, например, andps xmm0, xmm1 / orps xmm0, xmm2 для установки фиксированного значения в xmm0, которое зависит от вывода sqrtsd.

Или более простой способ проверки задержки заключается в использовании «преимущества» ложная выходная зависимость sqrtsd xmm0, xmm1 - это и sqrtss оставляют старшие 64/32 бита (соответственно) пункта назначения неизменными, таким образом, выходной регистр также является входом для этого слияния. Я предполагаю, что именно так ваша наивная попытка inline-asm закончилась узким местом с задержкой вместо пропускной способности , когда компилятор выбрал другой регистр для вывода, чтобы он мог просто перечитать тот же ввод в al oop , Встроенный ассемблер, который вы добавили в свой вопрос, полностью поврежден и даже не скомпилируется, но, возможно, ваш реальный код использовал "x" (регистр xmm) ввода и вывода ограничения вместо "i" (немедленно)?

Этот источник NASM для исполняемого теста stati c l oop (для запуска под perf stat) использует эту ложную зависимость с кодировкой не-VEX sqrtsd.

Это проектирование ISA благодаря Intel, оптимизирующей в краткосрочной перспективе с SSE1 на Pentium III. P3 внутренне обрабатывал 128-битные регистры как две 64-битные половины. Оставляя верхнюю половину неизменной, пусть скалярные инструкции декодируются в один моп. (Но это все еще дает PIII sqrtss ложную зависимость). Наконец, AVX позволяет нам избежать этого с помощью vsqrtsd dst, src,src, по крайней мере, для регистровых источников, и аналогично vcvtsi2sd dst, cold_reg, eax для аналогично близоруких разработанных инструкций скалярного преобразования int-> fp. (G CC отчеты о пропущенной оптимизации: 80586 , 89071 , 80571 .)


На многих более ранних процессорах даже пропускная способность была переменной, но Skylake усилил разделители настолько, что планировщик всегда знает, что может запустить новый цикл div / sqrt uop через 3 цикла после последнего ввода с одинарной точностью.

Даже пропускная способность Skylake с двойной точностью является переменной, хотя: от 4 до 6 циклов после последнего ввода ввода с двойной точностью, если Таблицы команд Agner Fog верны. https://uops.info/ показывает фиксированную взаимную пропускную способность 6 c. (Или вдвое длиннее для 256-битных векторов; 128-битные и скалярные могут использовать отдельные половины широких разделителей SIMD для большей пропускной способности, но с той же задержкой.) См. Также Деление с плавающей запятой против умножения с плавающей запятой для некоторые цифры пропускной способности / задержки, извлеченные из таблиц команд Agner Fog.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...