了解 Linux perf FP 计数器和 C++ 程序中的 FLOPS 计算

Understanding Linux perf FP counters and computation of FLOPS in a C++ program

我正在尝试测量在 C++ 程序中执行的计算量 (FLOPS)。我正在使用基于 Broadwell 的 CPU 而不是使用 GPU。我尝试了以下命令,其中包含了我发现的所有与 FP 相关的事件。

perf stat -e fp_arith_inst_retired.128b_packed_double,fp_arith_inst_retired.128b_packed_single,fp_arith_inst_retired.256b_packed_double,fp_arith_inst_retired.256b_packed_single,fp_arith_inst_retired.double,fp_arith_inst_retired.packed,fp_arith_inst_retired.scalar,fp_arith_inst_retired.scalar_double,fp_arith_inst_retired.scalar_single,fp_arith_inst_retired.single,inst_retired.x87 ./test_exe

我得到如下信息:

 Performance counter stats for './test_exe':

                 0      fp_arith_inst_retired.128b_packed_double    (36.36%)
                 0      fp_arith_inst_retired.128b_packed_single     (36.36%)
                 0      fp_arith_inst_retired.256b_packed_double     (36.37%)
                 0      fp_arith_inst_retired.256b_packed_single     (36.37%)
     4,520,439,602      fp_arith_inst_retired.double     (36.37%)
                 0      fp_arith_inst_retired.packed     (36.36%)
     4,501,385,966      fp_arith_inst_retired.scalar     (36.36%)
     4,493,140,957      fp_arith_inst_retired.scalar_double     (36.37%)
                 0      fp_arith_inst_retired.scalar_single     (36.36%)
                 0      fp_arith_inst_retired.single     (36.36%)
        82,309,806      inst_retired.x87              (36.36%)

      65.861043789 seconds time elapsed

      65.692904000 seconds user
       0.164997000 seconds sys

问题:

  1. 虽然C++程序是一个大工程,但我没有使用任何SSE/AVX指令。我不熟悉 SSE/AVX 指令集。该项目只是由“普通”C++编写的。为什么它包含许多fp_arith_inst_retired.doublefp_arith_inst_retired.scalarfp_arith_inst_retired.scalar_double?这些计数器与 SSE/AVX 计算有关,对吗?
  2. 括号中的百分比是什么意思?比如 (36.37%)
  3. 如何根据 perf 结果在我的 C++ 程序中计算 FLOPS?

谢谢。

C++ 编译器在 x86-64 上进行 FP 数学运算的正常方法是使用 SSE 指令的标量版本,例如addsd xmm0, [rdi] (https://www.felixcloutier.com/x86/addsd)。只有旧版 32 位版本默认使用 x87 FPU 进行标量数学计算。

如果您的编译器无法自动矢量化任何内容(例如,您没有使用 g++ -O3 -march=native),并且您唯一要做的数学运算是使用 double 而不是 float , 那么所有的数学运算都将使用标量双精度指令完成。

每个这样的指令将被 fp_arith_inst_retired.double.scalar.scalar-double 事件计数。它们重叠,基本上是同一事件的子过滤器。 (FMA 操作计为两个,即使它们仍然只有一条指令,所以这些是 FLOP 计数而不是 uops 或指令)。

所以你在 65.86 秒内有 4,493,140,957 次失败。
4493140957 / 65.86 / 1e9 ~= 0.0682 GFLOP/s,即 非常 低。

如果您有 128b_packed_double 的任何计数,您会将其乘以 2。如 perf list 描述中所述:“每个计数代表 2 个计算操作,每个元素一个”,因为一个 128 位向量包含两个 64 位 double 元素。所以这个 even 的每个计数都是 2 FLOPs。与其他人类似,遵循 perf list 输出中描述的比例因子,例如256b_packed_single.

的 8 次

因此您确实需要按类型和宽度分隔 SIMD 事件,但您可以只查看 .scalar 而无需分隔单双。

另见 FLOP measurement, one of the duplicates of FLOPS in Python using a Haswell CPU (Intel Core Processor (Haswell, no TSX)) which was linked on your previous question


(36.37%) 是在硬件计数器上编程的总时间的多少。您使用的事件多于计数器,因此 perf 为您多路复用了它们,经常交换并根据该统计采样进行推断以估计 运行 时间内的总数。参见 Perf tool stat output: multiplex and scaling of "cycles"

您可以通过省略给定构建的零事件来获得非零非冗余事件的准确计数。