Похоже, вам нужен способ оценить, насколько ваш код привязан к FPU или насколько эффективно вы используете FPU, а не подсчитывать количество флопов в соответствии с тем же анахроничным определением «флопа». Другими словами, вам нужен показатель, который достигает того же пика, если каждый модуль с плавающей запятой работает на полную мощность каждый цикл. Давайте посмотрим на Intel Sandy Bridge, чтобы увидеть, как это может вытряхнуть.
Аппаратно поддерживаемые операции с плавающей запятой
Этот чип поддерживает инструкции AVX , поэтому регистры имеют длину 32 байта (содержит 4 двойных). Суперскалярная архитектура позволяет инструкциям перекрываться, причем большинство арифметических инструкций выполняется за несколько циклов, даже если новая инструкция может быть запущена в следующем цикле. Эта семантика обычно сокращается путем записи задержки / обратной пропускной способности, значение 5/2 будет означать, что для выполнения инструкции требуется 5 циклов, но вы можете начинать новую инструкцию каждый второй цикл (при условии, что операнды доступны, поэтому нет данных зависимость и не дожидаясь памяти).
Существует три арифметических единицы с плавающей запятой на ядро, но третья не имеет отношения к нашему обсуждению, мы назовем соответствующие две единицы A и M, потому что их основными функциями являются сложение и умножение. Пример инструкции (см . Таблицы Агнера Фога )
vaddpd
: сложенное сложение, занимающее блок А в течение 1 цикла, задержка / обратная пропускная способность 3/1
vmulpd
: упакованное умножение, единица М, 5/1
vmaxpd
: упаковано выбрать попарно максимум, единица A, 3/1
vdivpd
: упакованное деление, единица М (и немного А), от 21/20 до 45/44 в зависимости от входа
vsqrtpd
: упакованный квадратный корень, некоторые A и M, 21/21 до 43/43 в зависимости от входа
vrsqrtps
: упакованный обратный квадратный корень низкой точности для ввода с одинарной точностью (8 floats
)
Точная семантика того, что может перекрываться vdivpd
и vsqrtpd
, по-видимому, неуловима и AFAIK, нигде не документирована. В большинстве случаев я думаю, что вероятность перекрытия невелика, хотя формулировка в руководстве предполагает, что в этой инструкции несколько потоков могут предложить больше возможностей для перекрытия. Мы можем достичь пиковых флопов, если мы начнем a vaddpd
и vmulpd
на каждом цикле, всего 8 флопов за цикл. Плотная матрица-матрица multiply ( dgemm
) может быть достаточно близко к этому пику.
При подсчете флопов для специальных инструкций я бы посмотрел, сколько заняты FPU. Предположим для аргумента, что в вашем диапазоне ввода vdivpd
потребовалось в среднем 24 цикла для завершения, полностью занимая модуль M, но сложение могло (если оно было доступно) выполняться одновременно для половины циклов. FPU способен выполнять 24 упакованных умножения и 24 упакованных сложения в течение этих циклов (с идеальным чередованием vaddpd
и vmulpd
), но при vdivpd
этом лучшее, что мы можем сделать, - это 12 дополнительных упакованных сложений. Если мы предположим, что лучший способ сделать деление - это использовать аппаратное обеспечение (разумное), мы можем посчитать vdivpd
36 упакованных «флопов», указывая, что мы должны считать каждое скалярное деление как 36 «флопов».
При использовании обратного квадратного корня иногда возможно превзойти аппаратное обеспечение, особенно если полная точность не требуется или диапазон входных данных узок. Как упоминалось выше, vrsqrtps
инструкция очень недорога, поэтому (если с одинарной точностью) вы можете выполнить одну, vrsqrtps
а затем одну или две итерации Ньютона для очистки. Эти итерации Ньютона просто
y *= (3 - x*y*y)*0.5;
Если многие из этих операций необходимо выполнить, это может быть значительно быстрее, чем наивная оценка y = 1/sqrt(x)
. До появления аппаратного приблизительного квадратного корня, некоторый чувствительный к производительности код использовал печально известные целочисленные операции, чтобы найти начальное предположение для итерации Ньютона.
Предоставляемые библиотекой математические функции
Мы можем применить похожую эвристику к математическим функциям, предоставляемым библиотекой. Вы можете профилировать, чтобы определить количество инструкций SSE, но, как мы уже обсуждали, это еще не все, и программа, которая тратит все свое время на оценку специальных функций, может не показаться близкой к пиковой, что может быть правдой, но это не так. бесполезно говорить, что все время уходит из-под вашего контроля на FPU.
Я предлагаю использовать хорошую библиотеку векторной математики в качестве основы (например, VML от Intel, часть MKL). Измерьте количество циклов для каждого вызова и умножьте на пиковые достижимые провалы за это количество циклов. Поэтому, если для оценки упакованной экспоненты требуется 50 циклов, посчитайте, что она равна 100 флопсам, умноженным на ширину регистра. К сожалению, библиотеки векторной математики иногда трудно вызвать и не имеют всех специальных функций, поэтому вы можете в конечном итоге делать скалярную математику, и в этом случае вы будете считать нашу гипотетическую скалярную экспоненту 100 флопсами (даже если это, вероятно, все еще занимает 50 циклов, так что вы получите только 25% «пика», если все время тратится на оценку этих показателей).
Как уже упоминалось, вы можете подсчитывать циклы и счетчики аппаратных событий, используя PAPI или различные интерфейсы. Для простого подсчета циклов вы можете прочитать счетчик циклов напрямую, используя rdtsc
инструкцию с фрагментом встроенной сборки.