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系统,可以使用cpupower
和turbostat
来获取更多频率相关信息, 如果没有这两个工具,可以使用下面的命令安装。
yum install -y linux-tools
相比lscpu
, cpupower frequency-info
除了输出频率信息外,它还能告诉我们可用的能耗策略有performance
和powersave
, 当前能耗策略设定为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的任何说明,所以推测应该是不能开放给实例控制。俗话说,成败在于细节,从这个细节上大概也算是能看到差距吧。