“fp operation”是指“浮点运算”。我正在研究一个Linux机器。是否有系统调用将此值作为静态度量标准返回,还是可以使用C / C ++ /其他语言的算法对其进行测试?
编辑:我应该提到这不是为了测试代码的效率。我在接受采访时被问到理论算法运行需要多长时间。我必须弄清楚将执行多少FLOPS并将其乘以每次操作所需的时间来得出粗略估计。我只是觉得这是一个有趣的问题。
答案 0 :(得分:4)
这几乎肯定不是一个有用的指标。许多其他事情都涉及到代码效率 - 特别是缓存命中/未命中。
话虽如此,有关此主题的ServerFault thread链接到您可以使用的Intel benchmarking suite,但您应该知道您可能看不到任何关联系统的最大FLOPS和应用程序的性能。
答案 1 :(得分:1)
通过在执行某些操作之前和之后获取时间戳,并通过减去它们以查看消耗了多少时间,有一种快速的方法。但是,这不是很准确。
答案 2 :(得分:1)
您想要使用的是Agner Fog的软件"Test programs for measuring clock cycles and performance monitoring"。这是他用来衡量指令的延迟和吞吐量来产生他着名的instruction tables。它有详细记录,包括您可以在自己的代码中使用的设备驱动程序(以及如何安装它们的说明)。这特别有用,因为为了测量某些数量,例如实际CPU频率,您需要访问哪些用户级代码通常无法访问的model specific registers (MSR)。
编辑:根据您对面试问题的编辑来估计运行浮点密集型操作所需的时间,您可以使用以下公式:
time = efficiency * number_of_floating_point_operations / peak_flops.
许多处理器的每个核心的峰值触发器可以从此链接flops-per-cycle-for-sandy-bridge-and-haswell-sse2-avx-avx2
中找到这些数量中的每一个都可能难以计算/估算,但效率最难,因为它可能取决于许多因素,例如:
为了使这个更清楚,让我们考虑两种算法:矩阵乘法和点积。计算这两种算法的浮点运算次数很容易。矩阵乘法的浮点运算数为2*n*n*n
。对于点积,它是2 * n。
矩阵乘法如果完成,它是计算绑定可以完全受益于SIMD和MIMD。 小n和低大的n的效率将从低开始。我自己的实现高达75%。英特尔MKL的使用率超过95%(但使用FMA的比例不到90%)。
因此,对于大n的矩阵乘法时间的后缀估计是假设100%效率给出 time = 2*n^3/peak_flops
。
然而,对于点积,小n的效率将开始高,大n的效率将降至平台。那是因为它受内存限制。因此,对于大n,效率取决于读取内存的速度。 For a modern machine that's about 10 GB/s。由于具有四个核心的现代桌面将具有超过100个GLOPS的峰值触发器且浮点数为4或8个字节,因此我估计大n
的效率接近1%,从而给出 time = 0.01*n/peak_flops
。我继续测试了这个(参见下面的代码)。我在我的系统上得到大约2.2 GLOPS,其峰值为236 GFLOPS,因此大约是峰值的1%。我的系统带宽约为11 GB / s。
大多数算法都受内存限制,因此了解系统读取内存的速度(DDR3,DDR4,...)是估算时间最有用的指标之一。
所以一般来说,如果你知道一个算法的浮点运算次数和你的处理器的峰值触发器,首先要问的是算法是计算绑定还是内存绑定大n然后是后面的 - 我将假设效率为100%的计算界限和内存限制的时间估计我将look up the bandwidth来估计效率。
此代码估算点积中的数据速率和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);
}
答案 3 :(得分:1)
以下提供了一个可变性的概念,我的一个基准测试使用相同汇编代码指令的长序列,尝试填充管道,不同的测试使用可变数量的寄存器。那么这只是为了补充。
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
答案 4 :(得分:0)
尝试确定FLOP“在真空中”所花费的时间没有多大意义,因为有很多其他因素影响它(操作数是否在内存/缓存/寄存器中,操作类型是什么)实际上,如果编译器正在发出x87 / SSE / SSE2 / ...指令,它是否涉及“奇怪的”IEEE754值,如果处理器管道被有效使用,如果代码是分支预测器友好的,...)
您应该使用分析器而不是算法的实际代码,以查看真正的瓶颈是什么,以及在特定代码中实际花费了多少时间 >