如何用好CPU
资源?
应用程序运行于操作系统之上。现在操作系统,无论是Linux
、Unix
或Windows
,分时复用是基本的CPU调度方式。本文仅就Linux
操作系统,结合Achelous平台,探讨如何高效使用CPU资源。
常见的灵魂拷问
资源cpus
在物理上对应什么?
Achelous平台上用户申请的CPU资源都是逻辑CPU核
,并不一定和任何物理CPU、核心或者超线程对应。如果没有打开核的超线程选项,物理CPU的每个核在操作系统中体现为一个逻辑CPU核
。如果打开了超线程技术,每个核会有两个物理线程,每个线程在操作系统中就是一个逻辑CPU核
。
例如在Linux
上运行命令cat /proc/cpuinfo
可以看到如下的信息:
[root@Cc1Apc ~]# cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 85
model name : Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz
stepping : 4
microcode : 0x2000022
cpu MHz : 1022.656
cache size : 19712 KB
physical id : 0
siblings : 28
core id : 0
cpu cores : 14
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 22
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm hwp hwp_act_window hwp_pkg_req intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local
bogomips : 4400.00
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 85
model name : Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz
stepping : 4
microcode : 0x2000022
cpu MHz : 1000.140
cache size : 19712 KB
physical id : 0
siblings : 28
core id : 1
cpu cores : 14
apicid : 2
initial apicid : 2
fpu : yes
fpu_exception : yes
cpuid level : 22
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb pln pts dtherm hwp hwp_act_window hwp_pkg_req intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local
bogomips : 4400.00
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:
...
上面显示的信息中,cpu cores
是14,而siblings
是28,说明硬件超线程技术已启用,每个核有两个物理线程。重要的信息如下:
processor
:逻辑CPU核的序号,保证唯一。本例中超线程技术启用,因此一个processor
对应着一个物理线程physical id
:该processor
所在的物理CPU的编号core id
:该processor
所在的核编号apicid
:用来区分不同逻辑核的编号,系统中每个逻辑核的此编号必然不同,此编号不一定连续。本例中,同一个physical id
和core id
会出现两次,唯有这个apicid
可以区分。
操作系统内核管理所有逻辑CPU核
,分时调用应用程序到逻辑CPU核
上运行。严格的说,应用程序申请的只是CPU
时间。所谓申请一个CPU
资源(cpus=1.0
),指的是操作系统内核调度器保证程序运行占用的CPU
时间等价于占用一个CPU
的100%。程序并不会运行在特定的CPU
上,也不一定会一直占用某个CPU
。
注意,从现在开始,我们所说的CPU
指的是逻辑CPU
核。
程序CPU
占用率是什么意思?
用户通过top
命令可以看到某个操作系统进程(process
)或者线程(thread
)的CPU
占用率。
对线程来说,它同时只能在一个CPU
上运行。它的占用率指的是某段时间内线程实际运行时间折算为一个逻辑CPU
核时间的比率,不超过100%。100%说明这个线程这段时间内一直在占用CPU
,不在这个CPU
上运行,就在那个CPU
上运行。
一个进程可能由多个线程组成(例如通过pthread进行多线程编程)。所以进程的CPU
利用率指的是这段时间所有线程的占用率总和,可以超过100%,但不会超过100 * 线程数 %
。
程序可以使用超过分配的CPU
资源吗?
Achelous平台使用linux
系统的CGroup
机制限制程序的CPU
利用率。它在系统不忙的时候,即存在大量空闲的CPU
资源的时候,允许程序使用超过申请的CPU
占用率。当系统繁忙,即所有的CPU
资源都被分配,且每个程序都可以在CPU
上运行,它从平均意义确保实际的CPU
占用率不会超过申请的CPU
数。
举个例子来说明。假设系统中CPU
数为2。你的任务申请了1个CPU
,但是程序启动了2个线程,且线程都是耗费CPU
的计算操作。在没有其它任务的情况下,你的实际CPU
占用率可能达到200%。如果另一个任务启动,也申请了一个CPU
,也是耗费CPU
时间的运算,最终每个任务的CPU
占用率都只能到100%。
为什么分配的CPU
资源与实际占用率不匹配?
用户常常问他的程序的CPU
占用率和申请的CPU
资源为什么不匹配?回答这个问题需要了解操作系统调度任务的原理。一般情况下,有效利用CPU
资源需要注意下面几个方面。
任务的线程数足够吗?
前面已经解释,一个线程最多占用一个CPU
。如果申请CPU
资源为3,但是程序只启动了2个线程,那么实际的CPU
占用率最多是200%,不可能达到期望的300%。
那么程序启动的线程数和CPU
占用率的关系是什么样的呢?这取决于线程主要在进行什么操作。如果程序启动了5个线程,其中3个线程执行的都是耗费CPU
的指令(例如科学计算),2个线程都是响应网络事件,则应该申请3个CPU
,最后CPU
占用率可能达到300%。
一般来讲建议申请的CPU
数应该等于折算成计算型线程的数量。
任务是否卡在IO上?
操作系统只会调度可运行(runnable
)的线程到CPU
上运行。所谓可运行,指的是线程没有卡在某些操作上。典型的引起等待的操作有:
• 等待事件或者锁:例如线程运行pthread_cond_wait
,等待的事件没有发生。
• 慢速的IO操作:例如线程进行文件读写(例如read
或者write
),读写操作等待存储或者文件系统响应。
• 等待异步操作结束:例如程序访问内存时,分配物理内存引起flush或者sync数据,操作完成才能得到可用的物理内存。则线程会被暂时调度出去。
当线程阻塞在事件上,操作系统不会将其调度到CPU
中运行。如果阻塞频繁发生,CPU
占用率就会下降。
任务分配的内存足够吗?
这种情况实质是第二种情况。如果分配给任务的物理内存不够,会引起频繁的换页操作(swap
),阻塞在IO上。这种情况下使用iotop
观察,会发现线程在swap
上的占用率比较高。
用户任务可以绑定CPU
吗?
Achelous平台上,调度器以进程或者容器方式启动用户任务的时候,并不会限制任务运行的CPU集合。换言之,Achelous不会使用cgroup
的cpuset
限制任务的CPU
亲和性(affinity
)。这不妨碍用户自己在程序中通过系统调用实现affinity
。我们不认为用户这样做能够得到显著的好处,反而有可能降低CPU
占用率。我们不建议普通用户这样做。如果用户必须这么做,我们乐意协助。