为什么相同的 gcc 编译选项在不同的计算机体系结构上会有不同的表现?

Why will the same compile options of gcc behave differently on different computer architecture?

我使用以下两个makefile来编译我的程序来做高斯模糊。

  1. g++ -Ofast -ffast-math -march=native -flto -fwhole-program -std=c++11 -fopenmp -o interpolateFloatImg interpolateFloatImg.cpp

  2. g++ -O3 -std=c++11 -fopenmp -o interpolateFloatImg interpolateFloatImg.cpp

我的两个测试环境是:

但是,第一个输出在 E5 上的速度是 2 倍,而在 i7 上的速度是 0.5 倍。 第二个输出在 i7 上表现得更快,但在 E5 上表现得更慢。

谁能解释一下?

这是源代码:https://github.com/makeapp007/interpolateFloatImg

我会尽快给出更多细节。

i7 上的程序将在 8 个线程上 运行。 我不知道这个程序在E5上会产生多少线程。

====更新====

我是这个项目原作者的队友,结果如下。

Arch-Lenovo-Y50 ~/project/ca/3/12 (git)-[master] % perf stat -d ./interpolateFloatImg lobby.bin out.bin 255 20
Kernel kernelSize  : 255
Standard deviation : 20
Kernel maximum: 0.000397887
Kernel minimum: 1.22439e-21
Reading width 20265 height  8533 = 172921245
Micro seconds: 211199093
Performance counter stats for './interpolateFloatImg lobby.bin out.bin 255 20':
1423026.281358      task-clock:u (msec)       #    6.516 CPUs utilized          
             0      context-switches:u        #    0.000 K/sec                  
             0      cpu-migrations:u          #    0.000 K/sec                  
         2,604      page-faults:u             #    0.002 K/sec                  
4,167,572,543,807      cycles:u                  #    2.929 GHz                      (46.79%)
6,713,517,640,459      instructions:u            #    1.61  insn per cycle           (59.29%)
725,873,982,404      branches:u                #  510.092 M/sec                    (57.28%)
23,468,237,735      branch-misses:u           #    3.23% of all branches          (56.99%)
544,480,682,764      L1-dcache-loads:u         #  382.622 M/sec                    (37.00%)
545,000,783,842      L1-dcache-load-misses:u   #  100.10% of all L1-dcache hits    (31.44%)
38,696,703,292      LLC-loads:u               #   27.193 M/sec                    (26.68%)
1,204,703,652      LLC-load-misses:u         #    3.11% of all LL-cache hits     (35.70%)
218.384387536 seconds time elapsed

这些是工作站的结果:

workstation:~/mossCAP3/repos/liuyh1_liujzh/12$  perf stat -d ./interpolateFloatImg ../../../lobby.bin out.bin 255 20
Kernel kernelSize  : 255
Standard deviation : 20
Kernel maximum: 0.000397887
Kernel minimum: 1.22439e-21
Reading width 20265 height  8533 = 172921245
Micro seconds: 133661220
Performance counter stats for './interpolateFloatImg ../../../lobby.bin out.bin 255 20':
2035379.528531      task-clock (msec)         #   14.485 CPUs utilized          
         7,370      context-switches          #    0.004 K/sec                  
           273      cpu-migrations            #    0.000 K/sec                  
         3,123      page-faults               #    0.002 K/sec                  
5,272,393,071,699      cycles                    #    2.590 GHz                     [49.99%]
             0      stalled-cycles-frontend   #    0.00% frontend cycles idle   
             0      stalled-cycles-backend    #    0.00% backend  cycles idle   
7,425,570,600,025      instructions              #    1.41  insns per cycle         [62.50%]
370,199,835,630      branches                  #  181.882 M/sec                   [62.50%]
47,444,417,555      branch-misses             #   12.82% of all branches         [62.50%]
591,137,049,749      L1-dcache-loads           #  290.431 M/sec                   [62.51%]
545,926,505,523      L1-dcache-load-misses     #   92.35% of all L1-dcache hits   [62.51%]
38,725,975,976      LLC-loads                 #   19.026 M/sec                   [50.00%]
 1,093,840,555      LLC-load-misses           #    2.82% of all LL-cache hits    [49.99%]
140.520016141 seconds time elapsed

====更新==== E5规格:

workstation:~$ cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
     20  Intel(R) Xeon(R) CPU E5-2650 v3 @ 2.30GHz
workstation:~$ dmesg | grep cache
[    0.041489] Dentry cache hash table entries: 4194304 (order: 13, 33554432 bytes)
[    0.047512] Inode-cache hash table entries: 2097152 (order: 12, 16777216 bytes)
[    0.050088] Mount-cache hash table entries: 65536 (order: 7, 524288 bytes)
[    0.050121] Mountpoint-cache hash table entries: 65536 (order: 7, 524288 bytes)
[    0.558666] PCI: pci_cache_line_size set to 64 bytes
[    0.918203] VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
[    0.948808] xhci_hcd 0000:00:14.0: cache line size of 32 is not supported
[    1.076303] ehci-pci 0000:00:1a.0: cache line size of 32 is not supported
[    1.089022] ehci-pci 0000:00:1d.0: cache line size of 32 is not supported
[    1.549796] sd 4:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    1.552711] sd 5:0:0:0: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    1.552955] sd 6:0:0:0: [sdc] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA

根据您指出的编译器标志,第一个 Makefile 使用了 -march=native 标志,这部分解释了为什么您观察到两个 CPU 有或没有旗帜.

此标志允许 GCC 使用指令特定于给定 CPU 架构,并且这些指令不一定适用于不同的架构。它还暗示 -mtune=native 为机器的特定 CPU 调整编译代码 并支持 运行 在 [=36] 上更快的指令序列=].请注意,如果 运行 在具有不同 CPU 的系统上,使用 -march=native 编译的代码可能 根本无法工作 ,或者速度明显变慢。

因此,即使选项看起来相同,它们在幕后的行为也会有所不同,具体取决于您用来编译的机器。您可以在 GCC documentation.

中找到有关此标志的更多信息

要查看每个CPU具体启用了哪些选项,您可以运行在每台机器上执行以下命令:

gcc -march=native -Q --help=target

此外,不同版本的 GCC 也会影响不同的编译器标志将如何优化您的代码,尤其是 -march=native 标志,它没有在旧版本的 GCC 上启用了许多调整(当时不一定完全支持较新的体系结构)。这可以进一步解释您观察到的差距。

你的程序有很高的缓存未命中率。对程序有利还是不利?

545,000,783,842 L1-dcache-load-misses:u # 所有 L1-dcache 命中的 100.10%

545,926,505,523 L1-dcache-load-misses # 所有 L1-dcache 命中的 92.35%

i7 和 E5 的缓存大小可能不同,所以这是差异的来源之一。其他是 - 不同的汇编代码、不同的 gcc 版本、不同的 gcc 选项。

您应该尝试查看代码内部,找到热点,分析命令处理了多少像素以及如何处理顺序可能对 cpu 和内存更好。重写热点(花费运行时间最多的代码部分)是解决任务的关键http://shtech.org/course/ca/projects/3/.

您可以在record / report / annotate模式下使用perf profiler来查找热点(如果您使用[重新编译项目会更容易) =15=] 添加选项):

# Profile program using cpu cycle performance counter; write profile to perf.data file
perf record ./test test_arg1 test_arg2
# Read perf.data file and report functions where time was spent 
#  (Do not change ./test file, or recompile it after record and before report)
perf report
# Find the hotspot in the top functions by annotation
#  you may use Arrows and Enter to do "annotate" action from report; or:
perf annonate -s top_function_name
perf annonate -s top_function_name > annotate_func1.txt

我能够在具有 2 个内核(4 个启用 HT 的虚拟内核)和 AVX2+FMA 的移动 i5-4*(英特尔 Haswell)上提高 7 次小 bin 文件和 277 个 10 个参数的速度。

需要重写一些循环/循环嵌套。您应该了解 CPU 缓存的工作原理以及它更容易做到的事情:经常丢失或不经常丢失。此外,gcc 可能是哑巴,可能并不总是检测读取数据的模式;可能需要此检测才能并行处理多个像素。