前言
在淘宝开源自己基于nginx打造的tegine服务器的时候,有这么一项特性引起了笔者的兴趣。“自动根据CPU数目设置进程个数和绑定CPU亲缘性”。当时笔者对CPU亲缘性没有任何概念,当时作者只是下意识的打开了google并输入CPU亲缘性(CPU Affinity)简单了做了个了解。
后来,在笔者参加实际工作以后,就碰到了这么两个问题。
问题一:如何在SMP的系统中,保证某个特定进程即使在其他进程都很忙的情况下都能够获得足够的CPU资源?解决的思路主要有以下两种:
- 提高进程的处理优先级
- 从SMP系统中,专门划拨出某一个CPU用于运行该程序。 而将其他进程划拨到其他的CPU上进行运行。
问题二:通过每日监控数据,我们发现服务器的CPU使用率出现这样子的情况,除了CPU0,其他CPU的负载都很低。
我们选择了通过设置CPU亲缘性的方式进行优化,在完成相关优化后,我们的应用程序性能得到了一定的提高。(大致有10%的性能提升)
此次,笔者借着博文的机会将“CPU亲缘性”这一特性的学习过程整理下来,以备日后查验。注意,本文所提到的CPU亲缘性均基于Linux。
什么是CPU亲缘性
所谓CPU亲缘性可以分为两大类:软亲缘性和硬亲缘性。
Linux 内核进程调度器天生就具有被称为 CPU 软亲缘性(soft affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。但不代表不会进行小范围的迁移。
CPU 硬亲缘性是指通过Linux提供的相关CPU亲缘性设置接口,显示的指定某个进程固定的某个处理器上运行。本文所提到的CPU亲缘性主要是指硬亲缘性。
使用CPU亲缘性的好处
目前主流的服务器配置都是SMP架构,在SMP的环境下,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被kernel调度到其他CPU上(即所谓的core migration),如此,CPU cache命中率就低了。设置CPU亲缘性,程序就会一直在指定的cpu运行,防止进程在多SMP的环境下的core migration,从而避免因切换带来的CPU的L1/L2 cache失效。从而进一步提高应用程序的性能。
Linux CPU亲缘性的使用
我们有两种办法指定程序运行的CPU亲缘性。
- 通过Linux提供的taskset工具指定进程运行的CPU。
- 方式二,glibc本身也为我们提供了这样的接口,借来的内容主要为大家讲解如何通过编程的方式设置进程的CPU亲缘性。
相关接口
利用glibc库中的sched_getaffinity接口,我们获取应用程序当前的cpu亲缘性,而通过sched_setaffinity接口则可以把应用程序绑定到固定的某个或某几cpu上运行。相关定义如下:
1 #include <sched.h> 2 3 4 void CPU_ZERO(cpu_set_t *set); 5 void CPU_CLR(int cpu, cpu_set_t *set); 6 void CPU_SET(int cpu, cpu_set_t *set); 7 int CPU_ISSET(int cpu, cpu_set_t *set); 8 9 int sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask); 10 11 int sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);