1.1 CPU与内存子系统性能调优简介

调优思路

性能优化的思路如下:

      l   如果CPU的利用率不高,说明资源没有充分利用,可以通过工具(如strace)查看应用程序阻塞在哪里,一般为磁盘,网络或应用程序的业务处理中存在休眠或信号等待,这些优化措施在后续其它章节描述。

      l   如果CPU利用率高,通过优化软件硬件的配置参数来更好适配业务场景,减少CPU占用率,让整个系统有更多的CPU时间来处理业务。

我们也可以选择更好的硬件,根据CPU的能力配置合适的内存条,建议内存满通道配置,发挥内存最大带宽:一颗鲲鹏920处理器的内存通道数为8,两颗鲲鹏920处理器的内存通道数为16;建议选择高频率的内存条,提升内存带宽:鲲鹏920在1DPC配置时,支持的内存最高频率为2933MHz。

主要优化参数

优化项

优化项简介

默认值

生效范围

鲲鹏916

鲲鹏920

优化应用程序的NUMA配置

在NUMA架构下,CPU core访问临近的内存时访问延迟更低。将应用程序绑在一个NUMA节点,可减少因访问远端内存带来的性能下降。

默认不绑定核

立即生效

yes

yes

修改CPU预取开关

内存预取在数据集中场景下可以提前将要访问的数据读到CPU cache 中,提升性能;若数据不集中,导致预取命中率低,则浪费内存带宽。

on

重启生效

no

yes

调整定时器机制

nohz机制可减少不必要的时钟中断,减少CPU调度开销。

不同OS默认配置不同

Euler:nohz=off

重启生效

yes

yes

调整内存的页大小为64K

内存的页大小越大,TLB中每行管理的内存越多,TLB命中率就越高,从而减少内存访问次数。

不同OS默认配置不同:

4KB或64K

重新编译内核、更新内核后生效

yes

yes

优化应用程序的线程并发数

适当调整应用的线程并发数,使得充分利用多核能力和资源争抢之间达到平衡。

由应用本身决定

立即生效或重启生效(由应用决定)

yes

yes

1.2 常用性能监测工具

1.2.1 top工具

介绍

top是最常用的Linux性能监测工具之一。通过top工具可以监视进程和系统整体性能。

命令参考举例:

命令

说明

top

查看系统整体的CPU、内存资源消耗。

top执行后输入1

查看每个CPU core资源使用情况。

top执行后输入F,并选择P选项

查看线程执行过程中是否调度到其它CPU core。

top -p $PID -H

查看某个进程内所有线程的CPU资源占用。

安装方式

系统自带,无需安装。

使用方法

                                步骤 1      使用top命令统计整体CPU、内存资源消耗。

l   CPU项:显示当前总的CPU时间使用分布。

  • us表示用户态程序占用的CPU时间百分比。

  • sy表示内核态程序所占用的CPU时间百分比。

  • wa表示等待IO等待占用的CPU时间百分比。

  • hi表示硬中断所占用的CPU时间百分比。

  • si表示软中断所占用的CPU时间百分比。

通过这些参数我们可以分析CPU时间的分布,是否有较多的IO等待。在执行完调优步骤后,我们也可以对CPU使用时间进行前后对比。如果在运行相同程序、业务情况下CPU使用时间降低,说明性能有提升。

l   KiB Mem:表示服务器的总内存大小以及使用情况。

l   KiB Swap:表示当前所使用的Swap空间的大小。Swap空间即当内存不足的时候,把一部分硬盘空间虚拟成内存使用。如果当前所使用的Swap空间大于0,可以考虑优化应用的内存占用或增加物理内存。

                                步骤 2      在top命令执行后按1,查看每个CPU core的使用情况。

通过该命令可以查看单个CPU core的使用情况,如果CPU占用集中在某几个CPU core上,可以结合业务分析触发原因,从而找到优化思路。

    步骤 3      选中top命令的P选项,查看线程运行在哪些 CPU core上。

在top命令执行后按F,可以进入top命令管理界面。在该界面通过上下键移动光标到P选项,通过空格键选中后按Esc退出,即可显示出线程运行的CPU核。观察一段时间,若业务线程在不同NUMA节点内的CPU core上运行,则说明存在较多的跨NUMA访问,可通过NUMA绑核进行优化。

 步骤 4      使用top -p $PID -H命令观察进程中每个线程的CPU资源使用。

“-p”后接的参数为待观察的进程ID。通过该命令可以找出消耗资源多的线程,随后可根据线程号分析线程中的热点函数、调用过程等情况

----结束

1.2.2 Perf工具

介绍

Perf工具是非常强大的Linux性能分析工具,可以通过该工具获得进程内的调用情况、资源消耗情况并查找分析热点函数。

命令参考举例:

命令

说明

perf top

查看当前系统中的热点函数。

perf sched record -- sleep 1 -p $PID

记录进程在1s内的系统调用。

perf sched latency --sort max

查看上一步记录的结果,以调度延迟排序。

安装方式

以CentOS为例,使用如下命令安装:

# yum -y install perf

使用方法

                                步骤 1      通过perf top命令查找热点函数。

该命令统计各个函数在某个性能事件上的热度,默认显示CPU占用率,可以通过“-e”监控其它事件。

l   Overhead表示当前事件在全部事件中占的比例。

l   Shared Object表示当前事件生产者,如kernel、perf命令、C语言库函数等。

l   Symbol则表示热点事件对应的函数名称。

通过热点函数,我们可以找到消耗资源较多的行为,从而有针对性的进行优化。

 步骤 2      收集一段时间内的线程调用。

perf sched record命令用于记录一段时间内,进程的调用情况。“-p”后接进程号,“sleep”后接统计时长,单位为秒。收集到的信息自动存放在当前目录下,文件名为perf.data。

   步骤 3      解析收集到的线程调度信息。

perf sched latency命令可以解析当前目录下的perf.data文件。“-s”表示进行排序,后接参数“max”表示按照最大延迟时间大小排序。

----结束

1.2.3 numactl工具

介绍

numactl工具可用于查看当前服务器的NUMA节点配置、状态,可通过该工具将进程绑定到指定CPU core,由指定CPU core来运行对应进程。

命令参考举例:

命令

说明

numactl -H

查看当前服务器的NUMA配置。

numactl -C 0-7 ./test

将应用程序test绑定到0~7核运行。

numastat

查看当前的NUMA运行状态。

安装方式

以CentOS为例,使用如下命令安装:

# yum -y install numactl numastat

使用方法

                                步骤 1      通过numactl查看当前服务器的NUMA配置。

从numactl执行结果可以看到,示例服务器共划分为4个NUMA节点。每个节点包含16个CPU core,每个节点的内存大小约为64GB。同时,该命令还给出了不同节点间的距离,距离越远,跨NUMA内存访问的延时越大。应用程序运行时应减少跨NUMA访问内存。

步骤 2      通过numactl将进程绑定到指定CPU core。

通过 numactl -C 0-15 top 命令即是将进程“top”绑定到0~15 CPU core上执行。

可以通过numastat命令观察各个NUMA节点的状态。

l   numa_hit表示节点内CPU核访问本地内存的次数。

l   numa_miss表示节点内核访问其他节点内存的次数。跨节点的内存访问会存在高延迟从而降低性能,因此,numa_miss的值应当越低越好,如果过高,则应当考虑绑核。

----结束

1.3 优化方法

1.3.1 NUMA优化,减少跨NUMA访问内存

原理

通过1.1 鲲鹏处理器NUMA简介章节可以看到不同NUMA内的CPU core访问同一个位置的内存,性能不同。内存访问延时从高到低为:跨CPU > 跨NUMA不跨CPU > NUMA内

因此在应用程序运行时要尽可能的避免跨NUMA访问内存,我们可以通过设置线程的CPU亲和性来实现。

修改方式

l   网络可以通过如下方式绑定运行的CPU core,其中$cpuMask是16进制的数,最右边的bit表示core0;$irq为网卡队列中断号。

echo $cpuMask > /proc/irq/$irq/smp_affinity_list

l   通过numactl启动程序,如下面的启动命令表示启动test程序,只能在CPU core 28到core31运行(-C控制)。

numactl -C 28-31 ./test

l   在C/C++代码中通过sched_setaffinity函数来设置线程亲和性。

l   很多开源软件已经支持在自带的配置文件中修改线程的亲和性,例如nginx可以修改nginx.conf文件中的worker_cpu_affinity参数来设置nginx线程亲和性。

1.3.2 修改CPU的预取开关

原理

局部性原理分为时间局部性原理和空间局部性原理:

l   时间局部性原理(temporal locality):如果某个数据项被访问,那么在不久的将来它可能再次被访问。

l   空间局部性原理(spatial locality):如果某个数据项被访问,那么与其地址相邻的数据项可能很快也会被访问。

CPU将内存中的数据读到CPU的高速缓冲Cache时,会根据局部性原理,除了读取本次要访问的数据,还会预取本次数据的周边数据到Cache里面,如果预取的数据是下次要访问的数据,那么性能会提升,如果预取的数据不是下次要取的数据,那么会浪费内存带宽。

对于数据比较集中的场景,预取的命中率高,适合打开CPU预取,反之需要关闭CPU预取。目前发现speccpu和X265软件场景适合打开CPU预取,STREAM测试工具、Nginx和数据库场景需要关闭CPU预取。

修改方式

按照B 进入BIOS界面的步骤进入BIOS,然后在BIOS的如下位置设置CPU的预取开关。

1.3.3 定时器机制调整,减少不必要的时钟中断

原理

在Linux内核2.6.17版本之前,Linux内核为每个CPU设置一个周期性的时钟中断,Linux内核利用这个中断处理一些定时任务,如线程调度等。这样导致就算CPU不需要定时器的时候,也会有很多时钟中断,导致资源的浪费。Linux 内核2.6.17版本引入了nohz机制,实际就是让时钟中断的时间可编程,减少不必要的时钟中断。

修改方式

执行cat /proc/cmdline查看Linux 内核的启动参数,如果有nohz=off关键字,说明nohz机制被关闭,需要打开。修改方法如下:

image.png

修改前后,可以通过如下命令观察timer_tick的调度次数,其中$PID为要观察的进程ID,可以选择CPU占用高的进程进行观察:

perf sched record -- sleep 1 -p $PID

perf sched latency -s max

输出信息中有如下信息,其中591字段表示统计时间内的调度次数,数字变小说明修改生效。

timer_tick:(97) | 7.364 ms | 591 | avg: 0.012 ms | max: 1.268 ms

                                步骤 1      在“/boot”目录下通过find -name grub.cfg找到启动参数的配置文件。

                                步骤 2      在配置文件中将nohz=off去掉。

                                步骤 3      重启服务器。

----结束

1.3.4 调整内存页的大小为64K,提升TLB命中率

原理

TLB(Translation lookaside buffer)为页表(存放虚拟地址的页地址和物理地址的页地址的映射关系)在CPU内部的高速缓存。TLB的命中率越高,页表查询性能就越好。

TLB的一行为一个页的映射关系,也就是管理了一个页大小的内存:

TLB管理的内存大小 = TLB行数 x 内存的页大小

同一个CPU的TLB行数固定,因此内存页越大,管理的内存越大,相同业务场景下的TLB命中率就越高。

修改方式

修改Linux内核编译选项,并重新编译:

image.png

修改前后可以通过如下命令观察TLB的命中率($PID为进程ID):

perf stat -p $PID -d -d -d

输出结果包含如下信息,其中1.21%和0.59%分别表示数据的miss率和指令的miss率。

1,090,788,717      dTLB-loads                #  520.592 M/sec
13,213,603      dTLB-load-misses          #    1.21% of all dTLB cache hits
669,485,765      iTLB-loads                #  319.520 M/sec
3,979,246      iTLB-load-misses          #    0.59% of all iTLB cache hits

                                步骤 1      执行make menuconfig。

                                步骤 2      选择PAGESIZE大小为64K。

Kernel Features-->Page size(64KB)

                                步骤 3      编译和安装内核。

参考https://bbs.huaweicloud.com/forum/thread-24362-1-1.html

----结束

1.3.5 调整线程并发数

原理

程序从单线程变为多线程时,CPU和内存资源得到充分利用,性能得到提升。但是系统的性能并不会随着线程数的增长而线性提升,因为随着线程数量的增加,线程之间的调度、上下文切换、关键资源和锁的竞争也会带来很大开销。当资源的争抢比较严重时,甚至会导致性能明显降。下面数据为某业务场景下,不同并发线程数下的TPS,可以看到并发线程数达到128后,性能达到高峰,随后开始下降。我们需要针对不同的业务模型和使用场景做多组测试,找到适合本业务场景的最佳并发线程数。

修改方式

不同的软件有不同的配置,需要根据代码实现来修改,这里举例几个常用开源软件的修改方法:

  • MySql可以通过innodb_thread_concurrency设置工作线程的最大并发数。

  • Nginx可以通过worker_processes参数设置并发的进程个数。

作者:莱德汪汪队

 

 

 

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐