性能调优经验分享

标签:性能优化    2472人阅读 评论(1)
分类:

(这个编辑器排版不太好,可以在底端附件下载word版)

1 系统性能定义

    当我们对一个系统进行性能测试时主要关注两个指标:

1)   吞吐量(Throughput):系统单位时间内可以处理的请求数。

2)   系统延迟(Latency):用户从发出请求到收到回复所需要的时间。

这两个指标有一定的相关性。通常延迟越低,则系统能支持的吞吐量就会越高,因为延迟低说明系统的处理速度快,这样单位时间内就可以完成更多请求的处理。当反过来就不一定成立了,当一个系统的吞吐量越高,系统延迟不一定越低,反而有可能越高。比如我们常通过批处理的技术来减少网络通信开销,提高系统的整体吞吐量,但这样同时也会增大系统延迟。所以我们在设计系统时需要根据业务需求对这两个指标进行一些权衡。

2 性能瓶颈定位及优化

  当系统出现性能瓶颈时,我们可以通过查看操作系统负载和进行程序性能监控这两个途径进行瓶颈定位。

2.1 系统瓶颈优化

2.1.1 CPU使用情况

CPU是计算机的重要计算资源,通常情况下操作系统的调度机制能保证各个CPU核心的负载均衡。但在一些特殊情况下,如不合理的程序设计、异常处理等,会存在CPU分配不均的问题,而单核负载过高很可能导致系统整体性能快速下降。我们可以通过vmstat和top这两个工具来查看CPU资源的使用情况。

Vmstat工具可输出系统目前的内存、swap分区、块设备IO、CPU等多种资源的使用情况,其输出如图1所示。

1.png图1. Vmstat示例

通过Vmstat可以获得较为全面的信息,对系统当前的负载情况有个全面的认知。

Top工具可以看到具体每个CPU核心使用情况和每个进程的CPU使用情况,其输出如图2所示。

2.png

图2.Top示例

Top输出的信息比较详细,在执行top时可以输入数字键1查看各个CPU核心具体的运行情况。我们主要关注wa和si这两项占用的比例,wa表示当前有较多读写请求等待处理中,该项过高时我们需要查看磁盘IO的使用情况,而si表示软中断处理,这一项在单个核上过高时我们就需要考虑进行软中断处理的负载均衡了。

通常我们在线上定位问题时会使用到pssh工具批量登录服务器进行操作,在pssh指令下是无法直接执行top命令的,需要加-b选项然后通过管道结合grep工具过滤出自己想要的内容。

多线程程序可以通过Linux系统提供API函数sched_setaffinity和sched_getaffinity设置或获取线程的可以使用的CPU核。具体使用示例参考:http://blog.csdn.net/honey_yyang/article/details/7848608

linux的SMP负载均衡是基于进程数的,每个cpu都有一个可执行进程队列,只有当其中一个cpu的可执行队列里进程数比其他cpu队列进程数多25%时,才会将进程移动到另外空闲cpu上,也就是说cpu0上的进程数应该是比其他cpu上多,但是会在25%以内。所以我们主动进行绑核操作是能够达到一定的负载均衡效果的,此外同一个工作线程的业务逻辑通常具有相同或相近的上下文,绑定核心之后可以提高Cache的命中率。

对于多个进程间的负载均衡可以通过taskset工具来调整,taskset支持对运行中的进程进行CPU亲和性的设置,具体使用示例参见:http://time-track.cn/taskset-command.html

在网络IO频繁的系统中,我们可能会观察到网卡软中断被集中在几个CPU核心上处理。默认情况下软中断处理是由系统服务irqbalance进行负载均衡的,所以如果要主动进行软中断亲和性的设置需要先把这个服务关闭,然后通过指令cat /proc/interrupts获取到设备对应的中断号,修改对应的smp_affinity文件进行设置,具体示例见:http://smilejay.com/2012/02/irq_affinity/

传统的SMP(对称多处理器)系统中,所有处理器都共享系统总线,因此当处理器的数目增大时,系统总线的竞争冲突加大,系统总线将成为瓶颈。NUMA技术有效结合了SMP系统易编程性和MPP(大规模并行)系统易扩展性的特点,较好解决了SMP系统的可扩展性问题,已成为当今高性能服务器的主流体系结构之一。该架构中有多个节点(node),每个节点拥有多个CPU,节点内部使用共享的内存控制器,节点之间是通过互联模块进行连接和信息交互。在该架构下每个CPU访问本地节点的内存速度最快,所以我们要尽量让内存的访问在本地节点中进行。

Linux系统提供了numactl工具进行节点亲和性的设置,可以主动设置进程的计算节点和内存存取节点。具体使用示例见:http://linux.51yip.com/search/numactlhttp://cenalulu.github.io/linux/numa/

2.1.2 磁盘IO情况

当CPU使用率不高但性能却上不去时,有可能就是系统在做大量的磁盘IO操作导致的。我们可以通过iostat和iotop工具来具体定位是否存在大量写磁盘的程序(通常会是写日志操作),然后进行异常处理或是调整日志级别。

Iostat可对各个设备的io信息进行统计并输出,输出信息如图3所示。

3.png

图3.iostat示例

Iotop可对当前运行的各个进程的IO情况进行统计输出,它给出的信息比较具体,运行示例如图4所示。

4.png

图4.iotop示例

iotop与top类似,在使用pssh批量执行时需要加-b选项。

2.1.3 网络负载情况

我们在做网络服务器的时候,通常会很关心网络的带宽和延迟。我们知道网卡和交换机什么型号,主机之间的物理距离是多少,理论上是知道带宽和延迟是多少的。但真正的带宽和延迟情况会有很多变数,比如网卡驱动、路由、丢包率、协议栈配置都会有较大影响。我们可以通过ethtoo、qperf、iptraf、netstat、nc和tcpdump等工具进行问题定位。

Ethtool可用于查询及设置网卡参数,运行示意图如图5所示。

5.png

图5.ethtool示例

通过ethtool我们可以查看网卡连线是否有问题(Link detected一项)和网卡设备的吞吐量有多大(speed)一项,具体使用示例见:http://man.linuxde.net/ethtool

Qperf工具用于测量两个节点间的网络延迟与带宽,使用示例如图6所示。具体使用教程见:http://blog.yufeng.info/archives/2234

6.png

图6.qperf示例

Iptraf工具可以实时监听各个网卡的流量情况,除了吞吐量统计外还提供了流量构成分析,可以直观的看到TCP、UDP、ICMP等协议流量的数据大小,也可以以流为单位进行流量大小统计,运行示例如图7所示。具体使用教程见:http://blog.csdn.net/quiet_girl/article/details/50777210

7.png

图7.iptraf示例

Netstat工具能提供网络连接、路由表和网络接口信息,其运行示例如图8所示。Netstat具体使用教程见:http://man.linuxde.net/netstat

8.png

图8.netstat示例

我们主要看接收队列(Recv-Q)和发送队列(Send-Q)的数据,如果这两栏的数据一直居高不下就说明我们的程序在收发数据处理过程中出现了阻塞,需要重点关注。

最后一列state显示的是连接的状态,当有大量的连接处于time_wait或close_wait时说明现在存在大量处于关闭状态的连接,这种情况很可能就是服务器和客户端不断地重新建立短连接造成的。

此时我们可以查看是否是进程的打开句柄数超过了系统限制。我们可以使用指令:

ll /proc/$pid/fd | wc -l

查看进程号为$pid的进程所打开的文件句柄数,或者是使用指令:

lsof –d ‘^mem,^cwd,^txt’|awk ‘{print $2}’|sort|uniq –c|sort –nr

查看系统所有活跃进程打开的文件句柄数。

我们可以使用ulimit工具来查看和设置系统对用户的一些资源使用限制,运行示例如图9所示。

9.png

图9.ulimit示例

图9中core file size用于控制程序异常退出时生成的core文件大小,可以通过将其设置为0或unlimited来控制是否生成core文件。Open files则是控制单个进程所能打开的文件句柄上限,当进程打开的文件句柄数超上限时会出现“too many open files”的异常信息,这时就需要考虑调整句柄数限制了。

我们可以通过指令:

ulimit -n $文件句柄限制

来设置句柄数限制,但这样修改只在当前shell会话中有效,一旦离开则会失效。若想永久生效,需要修改/etc/security/limits.conf文件,追加写入以下两行

soft nofile 32768

hard nofile 65535

再修改/etc/sysctl.conf文件,追加写入以下内容

fs.file-max=65535

然后执行systemctl –p使更改生效。并执行以下指令:

echo 65535 > /proc/sys/net/core/somaxconn

此外如果想要提高并发度,可以适当提高tcp协议栈建立连接的队列大小:

echo 1024 >  /proc/sys/net/ipv4/tcp_max_syn_backlog

在TCP连接中发送端发送完数据后要等接收端确认,如果网络延时很大,TCP接收发送窗口过小,确认的次数就会增多,导致性能不高,对网络的利用率下降。因此对于延迟大的网络,我们可适当上调窗口大小,减少确认包开销。对于延迟低但存在丢包的网络,可以适当下调窗口大小,减少TCP重传数据对网络带宽的影响。我们可以通过修改/etc/sysctl.conf文件来调整TCP接收发送窗口大小,向其追加写入以下内容:

net.core.wmem_default = 8388608

net.core.rmem_default = 8388608

net.core.rmem_max = 16777216

net.core.wmem_max = 16777216

然后执行systemctl –p使更改生效。

有时我们在进行机器连通性测试的时候会发现ping没有问题,但nc或是tcpdump都无法捕获到对端发送的数据,此时就有可能是防火墙设置导致的。

我们可以通过执行以下指令来关闭防火墙(这是比较暴力的方法,可以通过设置iptables来替代这种方法):

Systemctl stop firewalld.service

防火墙相关启动停止指令可以参考:https://www.linuxidc.com/Linux/2016-12/138979.htm

此外,内核的过滤器设置也有可能将流量丢弃,可以通过修改/etc/sysctl.conf文件来进行设置,向其追加写入以下内容来关闭过滤器。

net.ipv4.conf.all.rp_filter=0

net.ipv4.conf.default.rp_filter=0

然后执行systemctl –p使修改生效。

2.2 程序性能优化

2.2.1 程序性能瓶颈定位

Cache是提高计算机整体效率的关键部件,其访问速度比内存快数十倍,其利用率是影响程序性能的重要因素之一。我们可以通过perf工具来查看程序的cache使用情况。

Perf支持针对处理器相关性能指标与操作系统相关性能指标进行跟踪分析。Perf支持的事件可以通过执行指令“perf list”来列出,如图10所示。

10.png

图10.perf支持的事件类型

我们可以通过执行perf stat来对程序在一段时间内的性能状况进行统计,通过选项–e来指定我们想要跟踪的事件,多个事件使用逗号分割,如图11所示。

11.png

图11.perf stat示例

Perf top提供了对程序实时地性能事件追踪,执行实例如图12所示。

12.png

图12. Perf top示例

通过perf stat我们可以对程序的cache使用情况有个整体的认知,而通过perf top我们可以知道程序中哪些调用引发了较多的cache-miss,这样就可以有针对性地进行优化了。Perf的具体使用教程见:http://blog.csdn.net/zhangskd/article/details/37902159

通常我们程序大部分的性能损耗都是由少量的代码导致的,找准这部分代码将使得性能优化事半功倍。我们可以利用SystemTap/Perf+FlameGraph来生成火焰图定位程序的耗时操作。

火焰图的生成与使用介绍可参考:http://www.ruanyifeng.com/blog/2017/09/flame-graph.html

 

2.2.2 代码调优

这里给出几个代码优化经验,以供参考。

1)   循环优化

a)   存在多层嵌套循环时,将次数多的循环放在最内层,这样可以减少初始化和比较次数,减少开销。

b)   将与循环变量无关的计算都放到循环外,如数组长度的获取。

c)   使用寄存器变量(register类型)缓存中间计算结果,通常这个会由编译器自己进行优化。

2)   算式优化

a)   使用移位代替整数2的倍数的乘除

b)       使用位运算代替取模运算,如将哈希表的大小设置为,则可以将计算哈希桶的取模运算变成与的与运算。

3)   逻辑判断优化

a)   Switch语句按照出现频率排序,让最常用的放在最前面,减少无效判断。

b)   If语句将计算量少的条件放在前面,利用短路规则避免过多的计算。

c)   使用查表的方法替代复杂的条件计算。

d)   布尔逻辑优化,比如!a && !b 转换为!(a || b) 会减少计算。

4)   指令cache优化

a)   相关函数代码放在一块

b)   减少冗余代码

c)   简化调用关系,降低调用深度

d)   分支预测,将概率大的分支尽量放前面

e)   内联和宏函数不要太大

5)   数据cache优化

a)   Cache对齐,尽量减少跨越多个cache line的数据

b)   延迟计算,不要过早初始化,避免初始化后由于条件跳转未使用该变量的情况。

c)   减少指针随机访问的操作

6)   指令级并行

a)   SIMD,参考资料:http://share.onlinesjtu.com/mod/tab/view.php?id=303http://blog.csdn.net/fengbingchun/article/details/18460199

b)   Prefetch,参考资料:https://yq.aliyun.com/articles/8867

 

3 经验总结

Ø  找准瓶颈前不要着急动手

Ø  要保证代码的可读性,清晰胜于技巧


查看评论
1楼 黄彩云 2018-05-23 17:09:34 [回复]
这编辑器不是排版不好,简直是不能看啊。。。

发表评论
  • 评论内容:
      
    个人资料
    文章分类
    发表时间
    阅读排行
    评论排行
首页
团队介绍
发展历史
组织结构
MESA大事记
新闻中心
通知
组内动态
科研成果
专利
论文
项目
获奖
软著
人才培养
MESA毕业生
MESA在读生
MESA员工
招贤纳士
走进MESA
学长分享
招聘通知
招生宣传
知识库
文章
地址:北京市朝阳区华严北里甲22号楼五层 | 邮编:100029
邮箱:nelist@iie.ac.cn
京ICP备15019404号-1