Под «операцией fp» я подразумеваю «операцию с плавающей запятой». Я работаю над коробкой Linux. Существует ли системный вызов, который возвращает это значение в виде статической метрики, или вы можете проверить это с помощью алгоритма на C / C ++ / каком-либо другом языке?
редактироватьЯ должен был упомянуть, что это не для проверки эффективности моего кода. Во время интервью меня спросили, сколько времени займет теоретический алгоритм. Я должен был выяснить, сколько FLOPS будет выполнено и умножить это на время, которое потребуется каждой операции, чтобы получить приблизительную оценку. Я просто, хотя это был интересный вопрос.
То, что вы хотите использовать, это программное обеспечение Agner Fog «Тестовые программы для измерения тактов и контроля производительности». Это то, что он использовал, чтобы измерить задержку и пропускную способность инструкций, чтобы произвести его знаменитый таблицы команд. Он хорошо документирован и содержит драйверы устройств (с инструкциями по их установке), которые вы можете использовать в своем собственном коде. Это особенно полезно, потому что для измерения определенных величин, таких как реальная частота процессора, необходим доступ к модельные регистры (MSR) к какому пользовательскому коду обычно нет доступа.
Изменить: на основе вашего редактирования вопроса об интервью, чтобы оценить, сколько времени потребуется для выполнения операции с плавающей запятой, вы можете использовать эту формулу:
time = efficiency * number_of_floating_point_operations / peak_flops.
Пиковое число провалов на ядро для многих процессоров можно найти по этой ссылке флоп-за цикл-за-песчаного моста-и-Haswell-SSE2-AVX-AVX2
Каждую из этих величин может быть трудно рассчитать / оценить, но эффективность является наиболее сложной, поскольку она может зависеть от многих вещей, таких как:
Чтобы сделать это более понятным, давайте рассмотрим два алгоритма: матричное умножение и скалярное произведение. Для этих двух алгоритмов легко вычислить количество операций с плавающей запятой. Число операций с плавающей запятой для умножения матриц 2*n*n*n
, Для точечного произведения это 2 * n.
Умножение матриц, если оно выполнено правильно, оно связано с вычислениями, может полностью извлечь выгоду из SIMD и MIMD.
Его эффективность начнется с малого n и плато для большого n. Моя собственная реализация получает до 75%. Intel MKL получает более 95% (но менее 90% при использовании FMA).
Таким образом, обратная оценка времени для умножения матриц для больших n должна предполагать 100% эффективность, дающую time = 2*n^3/peak_flops
,
Тем не менее, для точечного произведения эффективность будет высокой для малых n и снизится до плато для больших n. Это потому что это связано с памятью. Таким образом, для больших n эффективность определяется тем, насколько быстро считывается память. Для современной машины это около 10 ГБ / с. Поскольку современный настольный компьютер с четырьмя ядрами будет иметь пиковые значения более 100 GLOPS, а значения с плавающей запятой равны 4 или 8 байтам, я бы оценил эффективность для больших n
около 1% дает time = 0.01*n/peak_flops
. Я пошел дальше и проверил это (см. Код ниже). Я получаю около 2,2 GLOPS в моей системе, которая имеет пик 236 GFLOPS, так что это около 1% от пика. Пропускная способность моей системы составляет около 11 ГБ / с.
Большинство алгоритмов связаны с памятью, поэтому знание того, насколько быстро ваша система может читать память (DDR3, DDR4, …), является одним из наиболее полезных показателей для оценки времени.
Таким образом, в общем, если вы знаете число операций с плавающей запятой алгоритма и пиковые флопсы вашего процессора, первое, что вы должны спросить, является ли алгоритм ограниченным вычислением или памятью для больших n, а затем для обратной части оценка конверта времени, которое я предположил бы, что эффективность была равна 100% для вычисления и для памяти, я бы посмотрите пропускную способность оценить эффективность.
Этот код оценивает скорость передачи данных и GFLOPS от точечного произведения.
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
float dot(float *x, float *y, int n) {
float sum = 0;
for(int i=0; i<n; i++) {
sum += x[i]*y[i];
}
return sum;
}
int main(){
const int LEN = 1 << 28;
float *x = new float[LEN];
float *y = new float[LEN];
for(int i=0; i<LEN; i++) { x[i] = 1.0*rand()/RAND_MAX - 0.5; y[i] = 1.0*rand()/RAND_MAX - 0.5;}
uint32_t size = 2*sizeof(float)*LEN;
clock_t time0 = clock();
float sum = dot(x,y,LEN);
clock_t time1 = clock();
double dtime = (double)(time1 - time0) / CLOCKS_PER_SEC;
double rate = 1.0*size/dtime*1E-9;
double flops = 2.0*LEN/dtime*1E-9;
printf("sum %f, dtime %f, rate %f, flops %f\n", sum, dtime, rate,flops);
}
Это почти наверняка не полезный показатель. Многие, многие другие вещи влияют на эффективность кода — особенно попадания / промахи кэша.
С этим, как говорится, есть Поток ServerFault по этой теме, которая ссылается на Набор тестов Intel что вы можете использовать, но вы должны знать, что вы, вероятно, не увидите никакой корреляции между максимальными FLOPS вашей системы и производительностью вашего приложения.
Существует быстрый и грязный метод: взять метки времени до и после выполнения некоторых операций и вычесть их, чтобы увидеть, сколько времени было потрачено. Однако это не совсем точно.
Ниже приводится представление об изменчивости из одного из моих тестов, в котором используются длинные последовательности одних и тех же инструкций кода сборки, при попытке заполнения конвейеров и различных тестах используется переменное число регистров. Тогда это только для дополнения.
Intel(R) Core(TM) i7-4820K CPU running at near 3.9 GHz
Speeds adding to 1 Register 2 Registers 3 Registers 4 Registers
32 bit Integer MIPS 4303 8553 11997 12294
32 bit Float MFLOPS 1304 2608 3866 3866 SP
64 bit Float MFLOPS 1304 2608 3866 3865 DP
32 bit MMX Int MIPS 7824 14902 14936 14902
32 bit SSE MFLOPS 5215 10431 15464 15463 SP
64 bit SSE2 MFLOPS 2608 5216 7732 7731 DP
32 bit SSE2 Int MIPS 15647 29803 29872 29803
64 bit SSE2 Int MIPS 7823 14902 14936 14902
Не имеет смысла пытаться определить время, затрачиваемое FLOP «в вакууме», поскольку на него влияет множество других факторов (находятся ли операнды в памяти / кэше / регистрах, какой тип операции это на самом деле , если компилятор испускает инструкции x87 / SSE / SSE2 / …, включает ли он «странные» значения IEEE754, если конвейеры процессора используются эффективно, если код дружествен к предсказателю ветвления, …).
Вместо этого вы должны использовать профилировщик поверх фактического кода вашего алгоритма, чтобы увидеть, каковы реальный узкие места и сколько времени на них тратится в вашем конкретном коде.