是否有一种编程方式来估计CPU执行fp操作所需的时间?

时间:2014-07-30 23:50:26

标签: c++ c linux cpu instructions

“fp operation”是指“浮点运算”。我正在研究一个Linux机器。是否有系统调用将此值作为静态度量标准返回,还是可以使用C / C ++ /其他语言的算法对其进行测试?

编辑:我应该提到这不是为了测试代码的效率。我在接受采访时被问到理论算法运行需要多长时间。我必须弄清楚将执行多少FLOPS并将其乘以每次操作所需的时间来得出粗略估计。我只是觉得这是一个有趣的问题。

5 个答案:

答案 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

中找到

这些数量中的每一个都可能难以计算/估算,但效率最难,因为它可能取决于许多因素,例如:

  1. 算法是计算还是内存绑定?
  2. 算法如何使用SIMD(例如SSE / AVX / FMA)?
  3. 算法如何使用MIMD(例如多核)?
  4. 您的实施使用不同的缓存级别的效果如何?
  5. 为了使这个更清楚,让我们考虑两种算法:矩阵乘法和点积。计算这两种算法的浮点运算次数很容易。矩阵乘法的浮点运算数为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值,如果处理器管道被有效使用,如果代码是分支预测器友好的,...)

您应该使用分析器而不是算法的实际代码,以查看真正的瓶颈是什么,以及在特定代码中实际花费了多少时间 >