Linux高性能网络编程十谈|性能优化(CPU和内存)

2023年 9月 17日 56.9k 0

上一篇文章讲了高性能编程的工具,这一篇我们基于前面的一些知识点和工具来聊一下Linux下的性能优化(本知识点分为两篇,当前主要介绍CPU和内存性能优化)。

第一部分:CPU和内存性能度量

图片

系统调用

这张图阐述一个应用程序需要经过这些模块调用,对于性能每一部分都可能会有影响,那么我们先需要了解每个模块需要怎么度量?

1、CPU度量

(1)CPU使用率
CPU使用率是最直观描述当前服务状态的情况,如果CPU使用率过高,则表示当前遇到了性能瓶颈,其中过高的这个具体值在线上一般是70%-90%之间,要么扩容服务,要么就排查性能问题。
查看性能工具有很多,最常用的是通过top -p 或者通过查看线程top -H -p 观察,另外可以使用上一篇的工具:mpstat -P ALL 1 2

(2)用户进程消耗CPU
用户进程消耗CPU是常见的情况,往往和业务代码或者使用的库相关,比如大量的循环,JSON解析大包等,在用户代码层有很多耗CPU的操作,都会表现CPU使用率异常,定位其问题可以通过以下方式:

  • 先通过ps或者top查询具体进程或者线程CPU消耗过高,然后查询pidstat -p 判断%usr %system %guest占比情况,判断是否为用户态消耗
  • 由于用户态涉及用户代码,可以通过perf top查看具体调用函数或者查看查看日志分析;

(3)内核消耗CPU
消耗CPU不止用户进程,还包括内核进程,系统调用等内核消耗CPU,可能的原因有大量的内存拷贝,锁,大量的上下文切换等等,具体分析和上面类似:

  • 先通过ps或者top查询具体进程或者线程CPU消耗过高,然后查询pidstat -p 判断%usr %system %guest占比情况,判断是否为内核态消耗;
  • 然后可以通过perf top或者strace查看系统调用情况,或者通过mpstat分析,总结中断或者上下文切换频率来判断;

(4)CPU等待
CPU花费在等待上的时间,主要是看是否大量的IO导致,也可以通过top定位具体进程,然后跟踪和分析该进程或者线程的网络调用情况。

(5)Nice消耗CPU
描述的是花费的re-nicing进程上时间占比,主要是更改了进程的执行顺序或者优先级。

(6)平均负载
平均负载是一个判断系统快慢的重要原因,可能往往不是某个进程引起的,主要有两个指标:

  • 队列中等待处理的进程数(TASK_RUNNING状态进程)
  • 等待不可中断任务被完成的进程数(TASK_UNINTERRUPTIBLE状态进程)

如果被阻塞,平均负载就会增加,可以通过uptime查看,往往负载增加这个时候需要优化代码或者增加机器资源。

(7)运行进程
当前运行和已经在队列中的进程数,往往进程过多会导致CPU调度繁忙,比如之前多进程的Apache Server,所以可以根据当前CPU的核数决定进程个数,一般繁忙情况下的进程不建议超过2倍CPU(当前空闲的进程也不宜过大,建议不超过10倍)。

(8)阻塞进程
阻塞进程是当前未达到执行条件的进程,和上面的CPU等待事件对应,一般是IO问题导致,比如写文件数据过慢,或者socket读写数据未到达等等情况,如何分析呢?可以通过strace跟踪系统调用分析。

(9)上下文切换
在系统上发生上下文切换的情况,也是判断CPU负载的重要因素,大量的上下文切换可能和大量中断或者锁相关,上下文切换会导致CPU的缓存被刷新,数据需要从内存换入换出等。
排查方案是通过perf或者vmstat工具查询,比如vmstat输出(也可以通过vmstat -s查看):

[root@VM-16-16-centos ~]# vmstat 2 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 298404  96824 1189732    0    0     1    34    1    0  0  0 99  0  0
 0  0      0 298284  96824 1189736    0    0     0   214  760 1315  1  0 99  1  0

其中system包括:CPU在内核态运行信息,包括in中断次数,cs上下文切换次数。

(10)中断
中断包含硬中断和软中断,硬中断是外设处理过程中产生的,通过硬件控制器通知cpu的状态变化,而软中断是通过模拟硬中断的一种信号处理方式,中断过多会导致CPU花费一些时间相应中断,这里也会影响性能,如何排查?通过命令行mpstat -P ALL 5 2可以查看:

[root@VM-16-16-centos ~]# mpstat -P ALL 5 2
Linux 4.18.0-348.7.1.el8_5.x86_64 (VM-16-16-centos)  2023年08月19日  _x86_64_ (2 CPU)

10时02分15秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
10时02分20秒  all    0.70    0.00    0.80    0.50    0.00    0.00    0.00    0.00    0.00   98.00
10时02分20秒    0    0.60    0.00    0.80    0.20    0.00    0.00    0.00    0.00    0.00   98.40
10时02分20秒    1    0.80    0.00    0.80    0.80    0.00    0.00    0.00    0.00    0.00   97.60

其中输出中包含的:

  • %irq:CPU处理硬中断的时间占比
  • %soft:CPU处理软中断的时间占比

2、内存度量

(1)空闲内存
通过free我们能看到当前内存情况:

[root@VM-0-11-centos ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3880192      407228      713024         872     2759940     3182872
Swap:             0           0           0
  • total:物理内存总量
  • used:已经使用的物理内存量
  • free:尚未使用的物理内存量
  • shared:被共享使用的物理内存量
  • buff:被缓存的物理内存量
  • cache:被缓存的硬盘文件的物理内存量
  • available:剩余可用的物理内存量,包括free + buff + cache - 系统预留的缓冲区
  • Swap total:交换空间总量
  • Swap used:已经使用的交换空间量
  • Swap free:尚未使用的交换空间量

从上面可以看出,free的内存越大越好,这样有剩余足够多的物理内存可以使用。

(2)Swap
Swap如上面说的是交换空间的内存数据,是linux为了释放一部分物理内存将数据临时保存在Swap空间中,通过vmstat -s查看具体信息如下:

[root@VM-16-16-centos ~]# vmstat -s
      1860492 K total memory
       274936 K used memory
       701576 K active memory
       707432 K inactive memory
       299040 K free memory
        96824 K buffer memory
      1189692 K swap cache
            0 K total swap
            0 K used swap
            0 K free swap
     12318019 non-nice user cpu ticks
       124590 nice user cpu ticks
     11848347 system cpu ticks
   2844992141 idle cpu ticks
      4677889 IO-wait cpu ticks
            0 IRQ cpu ticks
       208152 softirq cpu ticks
            0 stolen cpu ticks
     15879112 pages paged in
    985253486 pages paged out
            0 pages swapped in
            0 pages swapped out
   1330511648 interrupts
    260667271 CPU context switches
   1678004734 boot time
     58996940 forks

其中如果pages swapped inpages swapped out每秒增长很多大,表示内存上遇到了瓶颈,需要升级机器的内存或者优化代码。

(3)Slab
在Linux中,伙伴系统是以页为单位管理和分配内存,但是现实的需求却以字节为单位,假如我们需要申请20Bytes,总不能分配一页吧?那岂不是严重浪费内存。那么该如何分配呢?Slab分配器就应运而生了,专为小内存分配而生,Slab分配器分配内存以Byte为单位,但是Slab分配器并没有脱离伙伴系统,而是基于伙伴系统分配的大内存进一步细分成小内存分配,其作用如下:

  • 节省空间,减少内存碎片化,Slab对小对象进行分配,不用为每个小对象分配一页
  • 提高系统效率:当对象拥有者释放一个对象后,SLAB的处理是仅仅标记对象为空闲,并不做多少处理,而又有申请者申请相应大小的对象时,Slab会优先分配最近释放的对象

如果要排查Slab的详细信息,可以通过slabtop或者cat /proc/slabinfo,输出如下(执行slabtop):

 Active / Total Objects (% used)    : 1074142 / 1101790 (97.5%)
 Active / Total Slabs (% used)      : 39843 / 39843 (100.0%)
 Active / Total Caches (% used)     : 100 / 130 (76.9%)
 Active / Total Size (% used)       : 250498.05K / 253182.16K (98.9%)
 Minimum / Average / Maximum Object : 0.01K / 0.23K / 8.00K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
445302 445302 100%    0.10K  11418  39     45672K buffer_head
249102 249071  99%    0.19K  11862  21     47448K dentry
 83616  83557  99%    1.00K   5226  16     83616K ext4_inode_cache
 63240  40754  64%    0.04K    620 102  2480K ext4_extent_status
 54376  54297  99%    0.57K   3884  14     31072K radix_tree_node
 29547  29487  99%    0.19K   1407  21  5628K kmalloc-192
 28544  28488  99%    0.06K    446  64  1784K kmalloc-64
 21624  21624 100%    0.12K    636  34  2544K kernfs_node_cache
 20400  20400 100%    0.05K    240  85   960K shared_policy_node
 16276  15989  98%    0.58K   1252  13     10016K inode_cache
 10914  10914 100%    0.04K    107 102   428K selinux_inode_security
  7776   7776 100%    0.21K    432  18  1728K vm_area_struct
  7232   3921  54%    0.12K    226  32   904K kmalloc-128
  5376   5376 100%    0.02K     21 256        84K kmalloc-16
  5376   5376 100%    0.03K     42 128   168K kmalloc-32
  5120   5120 100%    0.01K     10 512        40K kmalloc-8
  4344   4306  99%    0.66K    362  12  2896K proc_inode_cache
  4096   4096 100%    0.03K     32 128   128K jbd2_revoke_record_s
  3822   3822 100%    0.09K     91  42   364K kmalloc-96
  3417   3217  94%    0.08K     67  51   268K anon_vma
  3344   3344 100%    0.25K    209  16   836K kmalloc-256
  3136   3136 100%    0.06K     49  64   196K ext4_free_data
  2190   2190 100%    0.05K     30  73   120K avc_xperms_node
  2112   2112 100%    1.00K    132  16  2112K kmalloc-1024
  • OBJS:由于Slab是按照object管理的,这里是对象数量
  • ACTIVE:当前活跃的objects数量
  • USE:缓存的利用率
  • OBJ SIZE:object的size的大小
  • SLABS:Slab的个数
  • OBJ/SLAB:每个Slab中object个数
  • CACHE SIZE:缓存大小,这里是不精确值,可以忽略
  • NAME:分配Slab的名字

我们可以从以上的信息中判断那些内核模块内存分配较多(比如OBJ SIZE过大),进而分析模块的性能瓶颈。

3、方法论

以下是我参照USE方法论整理排查性能度量指标流程,其中最大挑战点在于如何发现子模块中的问题并且分析问题?后续可以单独写一篇分析。

图片

方法论

第二部分:系统层优化

1、CPU

(1)缓存

#define N 2048

long timecost(clock_t t1, clock_t t2)
{
 long elapsed = ((double)t2 - t1) / CLOCKS_PER_SEC * 1000;
 return elapsed;
}

int main(int argc, char **argv)
{
 char arr[N][N];

 {
  clock_t start, end;
  start = clock();
  for (int i = 0; i 

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论