【问题标题】:Slowing down CPU Frequency by imposing memory stress通过施加内存压力来降低 CPU 频率
【发布时间】:2020-12-03 13:20:33
【问题描述】:

我强调了我的系统,看看它如何影响我使用 stress-ng 编写的一些程序。

程序本身是一个神经网络,主要由一些嵌套循环组成,进行一些乘法运算,并使用大约 1G 的 RAM,整体用 C++ 编码。

我对系统施加了一些内存压力:

stress-ng --vm 4 --vm-bytes 2G -t 100s

这会创建 4 个在 mmap 上旋转的工作人员,每个工作人员分配 2G 的 RAM。这会显着减慢我的程序的执行速度(从大约 150 毫秒到 250 毫秒)。但是程序变慢的原因不是缺少内存或内存带宽或其他东西。相反,CPU 周期从 3.4GHz(无压力)减少到 2.8GHz(有压力)。正如预期的那样,CPU 利用率大致保持不变 (99%)。

我用

测量了CPU频率
sudo perf stat -B ./my_program

有人知道为什么内存压力会降低 CPU 的速度吗?

我的 CPU 是 Intel(R) Core(TM) i5-8250U,我的操作系统是 Ubuntu 18.04。

亲切的问候 lpolari

【问题讨论】:

  • 查看英特尔页面 3.4GHz 是您的升压时钟,因此,如果您生成更多进程并且 CPU 因温度而减速,那么这不能解释吗?
  • 我不清楚当你说“慢下来”与什么相比?另外你怎么知道核心频率是性能下降的唯一原因或最大原因?就核心时钟周期而言,执行时间是多少?

标签: c++ linux cpu intel cpu-architecture


【解决方案1】:

请务必记住,现代 CPU(尤其是 Intel 制造的 CPU)具有可变的时钟频率。 CPU 在轻负载时会缓慢运行以节省电量,从而延长电池寿命,但在负载下会加速。

限制因素是热量,即只有在调整频率以降低功耗并进而减少热量产生之前,才允许 CPU 过热。

在具有多个内核的芯片上,单个内核可以非常快速地运行而不会遇到热节流。两个核心必须运行得更慢,它们产生的热量是有效的两倍,并且在使用所有四个核心时,每个核心必须共享总热预算的一小部分。

在运行测试时检查您的 CPU 温度是值得的,因为它可能会达到某种上限。

【讨论】:

  • 我怀疑这是热节流;更有可能是 Skylake 在保守的 EPP 设置(如默认设置)下故意减慢内存密集型工作负载。
  • 第一段可能具有误导性,因为它似乎暗示当活动核心数较少时,核心频率也会降低。活动核心的数量只是影响核心频率的因素之一。关于热限制,虽然您可能是对的,但很难说这是这种情况下的原因。核心频率限制可能有很多原因。即使所有 4 个内核都处于活动状态,具有适当冷却功能的 i5-8250U 也不应在 250 毫秒内达到热限制。我们需要查看turbostat的输出。
  • @HadiBrais 这就是为什么我建议监测温度将提供更多洞察力。
  • 但第二段自信地说“限制因素是热气流”,仅此而已。我说这可能是原因,但不一定。检查CPU温度不是一个坏主意,但最好看看turbostat的输出,它会直接告诉我们为什么会发生核心频率限制。
【解决方案2】:

Skylake 派生的 CPU 在负载/存储遇到瓶颈时会降低其核心时钟速度,而能源与性能设置有利于更省电。令人惊讶的是,您可以构建人为的情况,即使存储全部命中 L1d 缓存或从未初始化的内存加载(仍然 CoW 映射到相同的零页),也会发生这种降频。

Skylake 引入了对 CPU 频率的完全硬件控制(硬件 P-state = HWP)。 https://unix.stackexchange.com/questions/439340/what-are-the-implications-of-setting-the-cpu-governor-to-performance 频率决策可以考虑内部性能监控,它可以注意到诸如花费大多数周期停滞或停滞的事情。我不知道 Skylake 到底使用了什么启发式方法。

你可以通过循环一个大数组来重现这个1,而不需要进行任何系统调用。如果它很大(或者您在人工测试中跨越缓存行),perf stat ./a.out 将显示平均时钟速度低于正常的 CPU 绑定循环。


理论上,如果内存完全跟不上 CPU,降低核心时钟速度(并保持内存控制器不变)应该不会对性能造成太大影响。在实践中,降低时钟速度也会降低非核心时钟速度(环形总线 + L3 缓存),从而在一定程度上恶化内存延迟和带宽。

缓存未命中的部分延迟是从 CPU 内核获取请求到内存控制器,并且单核带宽受到最大并发(一个内核可以跟踪的未完成请求)/延迟的限制。 Why is Skylake so much better than Broadwell-E for single-threaded memory throughput?

例如我的 i7-6700k 在运行微基准测试时从 3.9GHz 下降到 2.7GHz,在默认启动设置下只有 DRAM 瓶颈。 (此外,它仅上升到 3.9GHz,而不是 4.0 全核或 4.2GHz,如 BIOS 中配置的那样,1 或 2 个内核处于活动状态,启动时默认 balance_power EPP 设置或 balance_performance。)

这个默认值似乎不太好,对于“客户端”芯片来说太保守了,因为单个内核几乎可以使 DRAM 带宽饱和,但只能以全时钟速度运行。或者在节能方面过于激进,如果你从另一个 POV 来看,尤其是对于像我的桌面这样具有高 TDP (95W) 的芯片,即使在运行 x265 视频编码等耗电的东西时也可以无限期地维持全时钟速度。 AVX2。

使用像 i5-8250U 这样的 ULV 15W 芯片可能更有意义,以便在 CPU 做更有趣的事情时留出更多的热量/功率余量。


这由他们的能源/性能偏好 (EPP) 设置管理。在默认的balance_power 设置下,这种情况相当强烈。 performance 完全不会发生这种情况,一些快速基准测试表明 balance_performance 也避免了这种省电减速。我在桌面上使用balance_performance

Ice Lake 之前的“客户端”(非至强)芯片将所有内核锁定在一起,因此它们以相同的时钟速度运行(如果其中一个运行不受内存限制的东西,它们都会运行得更高,例如 @987654332 @ 环形)。但是每个逻辑核心仍然有一个 EPP 设置。我一直只是更改所有内核的设置以保持它们相同:

在 Linux 上,读取设置:

$ grep . /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference
/sys/devices/system/cpu/cpufreq/policy0/energy_performance_preference:balance_performance
/sys/devices/system/cpu/cpufreq/policy1/energy_performance_preference:balance_performance
...
/sys/devices/system/cpu/cpufreq/policy7/energy_performance_preference:balance_performance

编写设置:

sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;
 do echo balance_performance > "$i"; done'

另见


脚注1:实验示例:

每个高速缓存行存储 1 个 dword,通过连续的高速缓存行直到缓冲区结束,然后将指针回绕到开头。对固定数量的存储重复此操作,而不管缓冲区大小。

;; t=testloop; nasm -felf64 "$t.asm" && ld "$t.o" -o "$t" && taskset -c 3 perf stat -d -etask-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,uops_issued.any,uops_executed.thread ./"$t"

;; nasm -felf64 testloop.asm
;; ld -o testloop testloop.o
;; taskset -c 3 perf stat -etask-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,uops_issued.any,uops_executed.thread -r1 ./testloop

; or idq.mite_uops 

default rel
%ifdef __YASM_VER__
;    CPU intelnop
;    CPU Conroe AMD
    CPU Skylake AMD
%else
%use smartalign
alignmode p6, 64
%endif

global _start
_start:

    lea        rdi, [buf]
    lea        rsi, [endbuf]
;    mov        rsi, qword endbuf           ; large buffer.  NASM / YASM can't actually handle a huge BSS and hit a failed assert (NASM) or make a binary that doesn't reserve enough BSS space.

    mov     ebp, 1000000000

align 64
.loop:
%if 0
      mov  eax, [rdi]              ; LOAD
      mov  eax, [rdi+64]
%else
      mov  [rdi], eax              ; STORE
      mov  [rdi+64], eax
%endif
    add  rdi, 128
    cmp  rdi, rsi
    jae  .wrap_ptr        ; normally falls through, total loop = 4 fused-domain uops
 .back:

    dec ebp
    jnz .loop
.end:

    xor edi,edi
    mov eax,231   ; __NR_exit_group  from /usr/include/asm/unistd_64.h
    syscall       ; sys_exit_group(0)

.wrap_ptr:
   lea  rdi, [buf]
   jmp  .back


section .bss
align 4096
;buf:    resb 2048*1024*1024 - 1024*1024     ; just under 2GiB so RIP-rel still works
buf:    resb 1024*1024 / 64     ; 16kiB = half of L1d

endbuf:
  resb 4096        ; spare space to allow overshoot

测试系统:Arch GNU/Linux,内核 5.7.6-arch1-1。 (以及来自 GNU Binutils 2.34.0 的 NASM 2.14.02、ld)。

  • CPU:i7-6700k Skylake
  • 主板:Asus Z170 Pro Gaming,在 BIOS 中配置为 1 或 2 核睿频 = 4.2GHz,3 或 4 核 = 4.0GHz。但是启动时的默认 EPP 设置是balance_power,它只能达到 3.9GHz。我的启动脚本更改为 balance_pwerformance,但仍只能达到 3.9GHz,因此风扇保持安静,但不那么保守。
  • DRAM:DDR4-2666(与没有缓存未命中的小测试无关)。

超线程已启用,但系统处于空闲状态,内核不会在另一个逻辑内核(我将其固定到的同级内核)上调度任何内容,因此它自己拥有一个物理内核。

然而,这意味着 perf 不愿意为一个线程使用更多可编程的 perf 计数器,因此perf stat -d 来监控 L1d 负载和替换,而 L3 命中/未命中意味着对 cycles 等的测量精度较低。它可以忽略不计,例如 424k L1-dcache-loads(可能在内核页面错误处理程序、中断处理程序和其他开销中,因为循环没有负载)。 L1-dcache-load-misses 实际上是 L1D.REPLACEMENT 甚至更低,比如 48k

我使用了一些性能事件,包括exe_activity.bound_on_stores -[存储缓冲区已满且没有未完成负载的循环]。 (有关说明,请参阅perf list,和/或英特尔手册了解更多信息)。

EPP:balance_power:从 3.9GHz 降频 2.7GHz

EPP 设置:balance_powersudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_power > "$i";done'

根据代码正在执行的操作进行节流;另一个核心上的暂停循环保持时钟高,这将在此代码上运行得更快。或者在循环中使用不同的指令。

# sudo ... balance_power
$ taskset -c 3 perf stat -etask-clock:u,task-clock,context-switches,cpu-migrations,page-faults,cycles,branches,instructions,uops_issued.any,uops_executed.thread,exe_activity.bound_on_stores -r1 ./"$t" 

 Performance counter stats for './testloop':

            779.56 msec task-clock:u              #    1.000 CPUs utilized          
            779.56 msec task-clock                #    1.000 CPUs utilized          
                 3      context-switches          #    0.004 K/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
                 6      page-faults               #    0.008 K/sec                  
     2,104,778,670      cycles                    #    2.700 GHz                    
     2,008,110,142      branches                  # 2575.962 M/sec                  
     7,017,137,958      instructions              #    3.33  insn per cycle         
     5,217,161,206      uops_issued.any           # 6692.465 M/sec                  
     7,191,265,987      uops_executed.thread      # 9224.805 M/sec                  
       613,076,394      exe_activity.bound_on_stores #  786.442 M/sec                  

       0.779907034 seconds time elapsed

       0.779451000 seconds user
       0.000000000 seconds sys

碰巧恰好是 2.7GHz。通常会有一些噪音或启动开销,而且会低一些。请注意,5217951928 个前端 uops / 2106180524 个周期 = 每个周期发出的约 2.48 个平均 uops,流水线宽度为 4,因此这不是低吞吐量代码。由于宏融合比较/分支,指令计数更高。 (我本可以展开更多,所以更多的指令是存储,更少的添加和分支,但我没有。)

(我重新运行了几次perf stat 命令,因此 CPU 不仅仅是在定时间隔开始时从低功耗睡眠中唤醒。间隔中仍然存在页面错误,但有 6 个页面错误在 3/4 秒的基准测试中可以忽略不计。)

balance_performance:全 3.9GHz,此 EPP 的最高速度

不会根据代码正在执行的操作进行限制。

# sudo ... balance_performance
$ taskset -c 3 perf stat -etask-clock:u,task-clock,context-switches,cpu-migrations,page-faults,cycles,branches,instructions,uops_issued.any,uops_executed.thread,exe_activity.bound_on_stores -r1 ./"$t" 

 Performance counter stats for './testloop':

            539.83 msec task-clock:u              #    0.999 CPUs utilized          
            539.83 msec task-clock                #    0.999 CPUs utilized          
                 3      context-switches          #    0.006 K/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
                 6      page-faults               #    0.011 K/sec                  
     2,105,328,671      cycles                    #    3.900 GHz                    
     2,008,030,096      branches                  # 3719.713 M/sec                  
     7,016,729,050      instructions              #    3.33  insn per cycle         
     5,217,686,004      uops_issued.any           # 9665.340 M/sec                  
     7,192,389,444      uops_executed.thread      # 13323.318 M/sec                 
       626,115,041      exe_activity.bound_on_stores # 1159.827 M/sec                  

       0.540108507 seconds time elapsed

       0.539877000 seconds user
       0.000000000 seconds sys

在时钟对时钟的基础上大致相同,尽管存储缓冲区已满的总周期略多。 (这是在核心和 L1d 缓存之间,而不是在核心之间,所以我们希望循环本身大致相同。使用 -r10 重复 10 次,这个数字在运行中是稳定的 +- 0.01%。)

performance: 4.2GHz,完全加速到最高配置频率

不会根据代码正在执行的操作进行限制。

# sudo ... performance
taskset -c 3 perf stat -etask-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,uops_issued.any,uops_executed.thread -r1 ./testloop

 Performance counter stats for './testloop':

            500.95 msec task-clock:u              #    1.000 CPUs utilized          
            500.95 msec task-clock                #    1.000 CPUs utilized          
                 0      context-switches          #    0.000 K/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
                 7      page-faults               #    0.014 K/sec                  
     2,098,112,999      cycles                    #    4.188 GHz                    
     2,007,994,492      branches                  # 4008.380 M/sec                  
     7,016,551,461      instructions              #    3.34  insn per cycle         
     5,217,839,192      uops_issued.any           # 10415.906 M/sec                 
     7,192,116,174      uops_executed.thread      # 14356.978 M/sec                 
       624,662,664      exe_activity.bound_on_stores # 1246.958 M/sec                  

       0.501151045 seconds time elapsed

       0.501042000 seconds user
       0.000000000 seconds sys

整体性能与时钟速度呈线性关系,因此与balance_power 相比,速度提升了约 1.5 倍。 (balance_performance 为 1.44,具有相同的 3.9GHz 全时钟速度。)

如果缓冲区大到足以导致 L1d 或 L2 缓存未命中,核心时钟周期仍然存在差异。

【讨论】:

  • 当CPU出现这种减速时,是不是类似AVX*频率许可的事情发生了? IE。触发减速的程序不受影响,但由于电源转换相对于代码执行(包括上下文切换)很慢,其他程序可能会受到影响(并且电源管理也有某种形式的滞后)。这就是 OP 神经网络可能发生的情况:它的 CPU 受限代码受到较低频率的影响。很好的答案顺便说一句,我不知道这种行为。
  • @MargaretBloom:一个内核上的 ALU/延迟绑定代码仍将保持所有内核固定在最大频率,即使它们正在运行内存绑定代码。至少在所有内核共享频率的前 Icelake“客户端”芯片上。 (不过,我只测试了一个单线程内存绑定进程和另一个单线程 pause 循环,而不是 所有 其他运行内存绑定代码的内核。)与 AVX turbo 许可证不同,它是纯粹是一种省电的启发式方法,而不是核心愿意让自己在某种情况下运行多快的上限。
  • “但是,这意味着 perf 不愿意为一个线程使用更多可编程性能计数器” - 我很确定 perf 在这里没有错:如果在 BIOS 中启用了 HT,则只有每个硬件线程有 4 个计数器可用,由 CPU 强制执行 AFAIK,无论当前是否正在运行第二个线程或类似的东西。如果启用 HT 而不是暂时不运行,它是您实际丢失的少数资源之一。
  • 您的第一个示例以 2.48 uops/cycle 运行,但仍在降频,非常有趣。然后降频有点令人惊讶:我认为他们使用的启发式方法类似于“请求未完成的停顿周期”,但这里应该基本上为零,因为 IPC 很高。也许有一个基于存储缓冲区占用的额外启发式或其他东西?当商店都进入 L1 时,会适得其反,因为这会随着频率 100% 扩展。
  • @BeeOnRope:是的,我期待提出一些例子来证明它在小缓冲区的情况下运行得很快,而只有在大缓冲区的情况下才能降频。这似乎是在选择降频启发式方法时的 CPU 性能错误。我认为exe_activity.bound_on_stores 比周期低很多表明存储缓冲区有时已满,但仅占总周期的一小部分,因此它确实是激进的降频。
【解决方案3】:

我最后一次看到这个时,它启用了允许处理器执行此操作的“节能 Turbo”设置。粗略地说,硬件监控每周期指令,如果增加的频率不会导致足够的吞吐量增加,则不会继续增加 Turbo 频率。对于 STREAM 基准,频率通常会下降几个 bin,但性能在渐近性能的 1% 以内。

我不知道英特尔是否记录了“Energy Efficient Turbo”设置如何与各种“Energy-Performance Preference”进行交互。在我们的生产系统中,“Energy Efficient Turbo”在 BIOS 中被禁用,但有时默认启用....

【讨论】:

  • 这是至强处理器上的,对吧?当核心时钟下降时,他们是否将非核心时钟保持在高位?在“客户端”芯片上,我认为非核心也会下降(除非你有另一个线程保持所有核心 + 非核心时钟高)。 IIRC,在 i7-6700k Skylake(具有硬件 P 状态)上,通过内存(使用 asm 循环)进行纯负载扫描的性能下降低于 1%。不过,我完全忘记了我的基准测试是 AVX,还是跨步标量负载,或者什么。
  • 用 NASM 测试代码更新了我的答案,结果来自 i7-6700k(SKL 客户端)。即使所有存储都命中 L1d 缓存,人工测试用例也可以重现效果,循环超过 16k 缓冲区!所以 SKL 不只是检查 IPC,因为这发生在 3.33 IPC(2.48 uops / 时钟)。此外,硬件 P 状态不仅仅是加速,它还将时钟降低到低于正常“库存”速度。
  • @PeterCordes 我对“Energy Efficient Turbo”的观察来自 Xeon E5 处理器(从 v3 开始)。对于高带宽工作负载,即使内核速度变慢,非内核频率也会自动保持在最大值。除了单线程延迟测试之外,这对于所有事情都是正确的——它们需要高频率,但由于非核心流量非常低,所以非核心频率较低。
猜你喜欢
  • 1970-01-01
  • 2019-11-13
  • 2011-05-15
  • 1970-01-01
  • 2023-03-27
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多