Linux系统优化与调试之四 - 云上云下的CPU频率

Posted by 云谷计算 on July 28, 2018

CPU频率是衡量其性能的最重要指标,大家一般习惯性的用MHZ或者GHZ描述,但随着现代CPU的SMP, TURBO BOOST, C-STATE, P-STATE等技术的广泛使用,CPU频率开始变得飘忽不定。我们从一颗Intel Core i7-6700HQ开始,一起来看看现代CPU的频率。


标称频率

Intel官网上,很容易查到该CPU的性能参数。这颗4核心处理器的基准频率是2.6 GHz, 最大Turbo频率能达到3.5 GHz, 可以看出,CPU频率是可以动态变化的,那么他究竟是如何动态调整呢?

指标
# of Cores 4
# of Threads 8
Processor Base Frequency 2.60 GHz
Max Turbo Frequency 3.50 GHz

实际运行频率

在系统空载运行情况下,用lscpu查看CPU频率: 最低运行频率是800 MHz, 最高可以到3500 MHz, 当前运行在大概800 MHz。

[root@hluo ~]# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                8
On-line CPU(s) list:   0-7
Thread(s) per core:    2
Core(s) per socket:    4
座:                 1
NUMA 节点:         1
厂商 ID:           GenuineIntel
CPU 系列:          6
型号:              94
型号名称:        Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
步进:              3
CPU MHz:             800.432
CPU max MHz:           3500.0000
CPU min MHz:           800.0000
BogoMIPS:            5184.00

在Linux系统,可以使用cpupowerturbostat来获取更多频率相关信息, 如果没有这两个工具,可以使用下面的命令安装。

yum install -y linux-tools

相比lscpu, cpupower frequency-info 除了输出频率信息外,它还能告诉我们可用的能耗策略有performancepowersave, 当前能耗策略设定为powersave

[root@hluo ~]# cpupower frequency-info
analyzing CPU 0:
  driver: intel_pstate
  CPUs which run at the same hardware frequency: 0
  CPUs which need to have their frequency coordinated by software: 0
  maximum transition latency:  Cannot determine or is not supported.
  hardware limits: 800 MHz - 3.50 GHz
  available cpufreq governors: performance powersave
  current policy: frequency should be within 800 MHz and 3.50 GHz.
                  The governor "powersave" may decide which speed to use
                  within this range.
  current CPU frequency: Unable to call hardware
  current CPU frequency: 800 MHz (asserted by call to kernel)
  boost state support:
    Supported: yes
    Active: yes

Linux内核会根据设定的能耗策略在硬件限定频率范围(800 MHz - 3.50 GHz)内调整频率,我的理解是: 系统负载升高,内核就会调高频率,争取在较短时间内把负载降下来;相反系统负载降低,内核就会把频率调低,这样能耗也就能随之降低。

  • powersave: 节能模式,内核会在系统空闲时将CPU频率调整到最小(800 MHz);
  • performance: 性能模式,内核保证CPU频率运行在基准频率(2.6 GHz)以上;

对于desktop, 特别是laptop, 我们一般应该把能耗策略设置为powersave, 否则电脑的散热风扇会转个不停,一会就没电了:) 而对于服务器,特别针对一些突发的延时敏感的应用,很低的运行频率肯定会大幅影响这些应用的性能,所以可以考虑使用performace模式。

我们可以通过sysfs来调整能耗策略,下面的命令将策略改成performance模式:

echo "performance" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

也可以通过sysfs调整CPU频率, 下面的脚本可以用来参考:

#!/bin/bash
# change cpu frequency

max_cap=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq`
min_cap=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq`

# lowest ~ 1.71GHz by default
max_freq=1710000
min_freq=$min_cap

# or you can specify by arguments
while getopts ":a::i:" opt; do
    case $opt in
    a)
        max_freq=$OPTARG
        ;;
    i)
        min_freq=$OPTARG
        ;;
    \?)
        echo "Invalid option -$OPTARG"
        echo "Usage: ./setcpu.sh [OPTIONS]"
        echo "Options:"
        echo -e "  -a:\tSpecify maximum CPU frequency"
        echo -e "  -i:\tSpecify minimum CPU frequency"
        exit 1
        ;;
    esac
done

if [ $max_freq -gt $max_cap ]; then
    echo "ERROR: Maximum CPU frequency $max_freq is higher than the highest value you can set: $max_cap"
    exit 2
fi

if [ $min_freq -lt $min_cap ]; then
    echo "ERROR: Minimum CPU frequency $min_freq is lower than the lowest value you can set: $min_cap"
    exit 2
fi

if [ $max_freq -gt 1710000 ]; then
    echo "WARNING: Maximum CPU frequency $max_freq is a bit too high"
fi

# set one core affects cpu 0, 1, 2, 3 simultaneously
su -c "echo $max_freq > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
su -c "echo $min_freq > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq"

echo "Maximum CPU frequency is set to `cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq`"
echo "Minimum CPU frequency is set to `cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq`"

Turbo(P-STAT)和TSC频率

P-STAT可以允许CPU在运行的时候动态调整频率,那么既然CPU的实际运行频率是动态的,我们考虑两个问题:

  • CPU的时钟频率TSC是动态的么?
  • CPU所有核心的最高频率是一致的么?

运行turbostat命令:

[root@hluo ~]# turbostat
turbostat version 17.06.23 - Len Brown <lenb@kernel.org>
CPUID(0): GenuineIntel 22 CPUID levels; family:model:stepping 0x6:5e:3 (6:94:3)
CPUID(1): SSE3 MONITOR - EIST TM2 TSC MSR ACPI-TM TM
CPUID(6): APERF, TURBO, DTS, PTM, HWP, HWPnotify, HWPwindow, HWPepp, No-HWPpkg, EPB
cpu2: MSR_IA32_MISC_ENABLE: 0x00850089 (TCC EIST No-MWAIT PREFETCH TURBO)
CPUID(7): SGX
cpu2: MSR_IA32_FEATURE_CONTROL: 0x00000005 (Locked )
CPUID(0x15): eax_crystal: 2 ebx_tsc: 216 ecx_crystal_hz: 0
TSC: 2592 MHz (24000000 Hz * 216 / 2 / 1000000)
CPUID(0x16): base_mhz: 2600 max_mhz: 3500 bus_mhz: 100
cpu2: MSR_MISC_PWR_MGMT: 0x00401cc0 (ENable-EIST_Coordination DISable-EPB DISable-OOB)
RAPL: 5825 sec. Joule Counter Range, at 45 Watts
cpu2: MSR_PLATFORM_INFO: 0x8083bf1011a00
8 * 100.0 = 800.0 MHz max efficiency frequency
26 * 100.0 = 2600.0 MHz base frequency
cpu2: MSR_IA32_POWER_CTL: 0x0024005d (C1E auto-promotion: DISabled)
cpu2: MSR_TURBO_RATIO_LIMIT: 0x1f202123
31 * 100.0 = 3100.0 MHz max turbo 4 active cores
32 * 100.0 = 3200.0 MHz max turbo 3 active cores
33 * 100.0 = 3300.0 MHz max turbo 2 active cores
35 * 100.0 = 3500.0 MHz max turbo 1 active cores
...
  • CPU TSC频率固定为2592 MHz,要不TSC计数器一会快一会慢的,时间计数肯定就不准了;

  • CPU的最高频率是3.5 GHz, 并不是所有核心都可以达到这个频率,其实只有一个核心能达到这个频率,4个核心能同时达到的最高频率只有3.1 GHz;

C-STATE

当CPU运行的时候就在C0状态,当CPU运行到HLT指令时, 就会从C0状态进入到C1状态, 从下表可以看出CPU在C1状态下功耗只有C0状态的30%, 但是需要付出的代价是如果再次运行指令,则需要额外的10ns延时。

电源状态 执行 唤醒时间 CPU功耗 平台 核心电压 缓存收缩 内容丢失
C0 0 正常 正常
C1 10ns 30% 正常 正常
C4 160000ns 2% I/0,无监控 C4_VID

C-STATE会对一些延时敏感的应用带来一些负面的影响, 可以通过在grub启动参数加入intel_idle.max_cstate=1,尝试将C-STATE的影响降至最低,甚至是使用intel_idle.max_cstate=0来关闭C-STATE。

#/etc/sysconfig/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=cl/root nomodeset rhgb quiet intel_idle.max_cstate=0"
GRUB_DISABLE_RECOVERY="true"

AWS云实例的CPU频率

AWS是目前No.1的公有云,它在细节上也做的最好。据我所知,目前只有AWS明确支持实例控制C-STAT和P-STAT。AWS针对如下实例类型提供了控制处理器 C 状态和 P 状态的功能:

  • 通用型: m4.10xlarge, m4.16xlarge
  • 计算优化: c4.8xlarge
  • 存储优化: d2.8xlarge, i3.8xlarge, i3.16xlarge, i3.metal, h1.8xlarge, h1.16xlarge
  • 加速计算: f1.16xlarge, g3.16xlarge, p2.16xlarge, p3.16xlarge
  • 内存优化: r4.8xlarge, r4.16xlarge, x1.16xlarge, x1.32xlarge, x1e.8xlarge, x1e.16xlarge, x1e.32xlarge

AWS以下实例类型提供了控制处理器 C 状态的功能:

  • 通用型: m5.12xlarge, m5.24xlarge, m5d.12xlarge, m5d.24xlarge
  • 计算优化: c5.9xlarge, c5.18xlarge, c5d.9xlarge, c5d.18xlarge

Intel将C-STAT和P-STAT设计用来帮助用户在功耗和性能上实现最佳平衡,但是对于云实例,估计没有用户会在意功耗,AWS将C-STAT和P-STAT开放给用户,AWS主要应该考虑的是当用户都选择性能最大化的时候,该如何搞定散热的问题 :)

  • AWS可以控制C-STAT和P-STAT的实例类型都是大规格,独占整个CPU, 这样就不会和其它租户产生冲突;
  • 如果实例可以控制P-STAT, 可以把能耗策略改成performance, 实现性能最大化;
  • 如果实例可以控制C-STAT, 可以在grub配置中加上intel_idle.max_cstate=0, 甚至进一步加上idle=poll,这样kernel会在idle状态的时候使用while(1)替代HLT,这样VM根本不会退出,实现应用延时最小化;

虚拟化(KVM)和其它云实例的CPU频率

当前upstream的KVM和Xen好像都还不能对VM呈现P-STAT, 登录国内某云服务器查看CPU频率,发现都运行在标称频率上:

[root@ecs-blog2 ~]# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                2
On-line CPU(s) list:   0,1
Thread(s) per core:    1
Core(s) per socket:    2
座:                 1
NUMA 节点:         1
厂商 ID:           GenuineIntel
CPU 系列:          6
型号:              63
型号名称:        Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz
步进:              2
CPU MHz:             2494.289
BogoMIPS:            4988.55

另外,sysfs的cpufreq子模块都是不可用状态,所以在实例里既不能设置能耗策略,也不能动态调整频率,频率应该完全由host策略控制。

[root@izr...]# cd /sys/devices/system/cpu && find .|grep cpufreq
[root@izr...]# echo $?
1

跟朋友一起翻看了下Linux对C-STAT支持的代码,我的理解是Linux会根据CPUID获取当前CPU能支持的C-STAT范围,然后填在EAX中作为参数调用MWAIT,CPU会根据这个参数选择进一个C-STAT(详情参见参考(3)); KVM Upsteram在因为MWAIT退出后,会忽略掉这个参数,进入内核的reschedule, 所以intel_idle.max_cstate=0在其他公有云实例和KVM虚拟机上应该是没有作用。

idle=poll这个参数很有趣,现在的Hypervisor肯定没法捕获guest的这个行为,它通过在guest内核中使用spin来实现自身不会被其他虚拟机抢占,如果在guest中设置了这个参数,host上看vcpu进程的使用率会一直保持在接近100%,这是一种典型的损人利己做法,现在国内很多云实例都使用了超分配,但是CPU QoS可能还非常不完善。如果绝大部分租户都意识到这样损人利己的手段,那么这就是对他们的技术考验了:)

我手上没有大规格的实例,我也没能从其它云厂商官方文档找到P-STAT/C-STAT的任何说明,所以推测应该是不能开放给实例控制。俗话说,成败在于细节,从这个细节上大概也算是能看到差距吧。

参考