在Linux下做性能分析1:基本模型

==介绍==

本Blog开始介绍一下在Linux分析性能瓶颈的基本方法。主要围绕一个基本的分析模型,介绍perf和ftrace的使用技巧,然后东一扒子,西一扒子,逮到什么说什么,也不一定会严谨。主要是把这个领域的一些思路和技巧串起来。如果读者来讨论得多,我们就讨论深入一点,如果讨论得少,那就当作一个提纲给我做一些对外交流用吧。

==基本模型==

我们需要有一套完整的方法来发现系统的瓶颈。“瓶颈”这个概念,来自业务目标,不考虑业务目标就没有“瓶颈”这个说法。从业务目标角度,通常我们的瓶颈出现在业务的通量(Throughput)和时延(Latency)两个问题上。(手机等领域常常还会考虑功耗这个要素,但那个需要另外的模型,这里暂时忽略)

比如一个MySQL数据库,你从其他机器上对它发请求,每秒它能处理10万个请求,这个就是通量性能,每个请求的反应时间是0.5ms,这个就是时延性能。

通量和时延是相互相承的两个量,当通量达到系统上限,时延就会大幅提高。我们从下面这个例子开始来看这个模型(图1):

请求从客户端发到计算机上,花t1的时间,用t2的时间完成计算,然后用t3的时间把结果送回到客户端,这个时延是t1+t2+t3,如果我们在t3发过来后,发下一个请求,这样系统的通量就是1/(t1+t2+t3)。

当然,现实中我们不可能使用这样的方法来处理业务请求,这样处理通量肯定是很低的。因为t2和t1/t3作用在两个不同的运行部件上,完全没有必要让他们串行(在同一个会话中有必要,但整体上没有必要。其原理就是CPU流水线)。所以这个模型应该这样来实现(图2):

t2变成一个队列的处理,这样时延还是会等于t1+t2+t3,但t2的含义变了。只要调度能平衡,而且认为通讯管道的流量无限,通量可以达到1/t2(和CPU流水线中,一个节拍可以执行一条指令的原理相同)。1/t2是CPU清队列库存的速度,数据无限地供给到队列上,如果瞬时队列的长度是len,CPU处理一个包的时间是ti,t2=len*ti,如果请求无限上升,len就会越来越长,时延就会变大,所以,通常我们对队列进行流控,流控有很多方法,反压丢包都可以,但最终,当系统进入稳态的时候,队列的长度就会维持在一个稳态,这时的t2就是可以被计算的了。

对于上面这样一个简单模型,如果CPU占用率没有到100%,时延会稳定在len*ti。len其实就等于接收线程(或者中断)一次收入的请求数。但如果CPU达到100%,队列的长度就无限增加,时延也会跟着无限增加。所以我们前面说,当通量达到上限的时候,时延会无限增加,直到发生流控。

现代CPU是一个多核多部件系统,这种队列关系在整个系统中会变得非常复杂,比如,它有可能是这样的(图3):

虽然很多系统看起来不是这样一个接一个队列的模型,但其实如果你只考虑主业务流,几乎大部分情形都是这样的

对这样的系统,我们仍有如下结论:系统的时延等于路径上所有队列的len[i]*t[i]的和。其实你会发现,这个模型和前一个简单模型本质上并没有区别。只是原来的原则作用在了所有的队列上。

也就是说:如果CPU没有占满,队列也没有达到流控,则时延会稳定在len[i]*t[i]上。我们只要保证输入短可以供数据到系统中即可

这个结论为我们的性能优化提供了依据。就是说,我们可以先看CPU是否满载,满载就优化CPU,没有满载就找流控点,通过流控点调整时延和通量就可以了。

在多核的情况下,CPU无法满载还会有一个原因,就是业务线程没有办法在多个CPU上展开,这需要通过增加处理线程来实现。

==全系统的线程模型==

我们一般看系统是从“模块”的角度来看的,但看系统的性能模型,我们是从线程的角度来看的。

这里的线程是广义线程,表示所有CPU可以调度以使用自己的执行能力的实体,包括一般意义的线程,中断,signal_handler等。

好比前面的MySQL的模型,请求从网卡上发送过来。首先是中断向量收到包请求,然后是softirq收包,调用napi的接口来收包,比如napi_schedule。这时调用进入网卡框架层了,但线程上下文还是没有变,napi_schedule回过头调用网卡的polling函数,网卡收包,通过napi_skb_finish()一类的函数向IP层送包,然后顺着比如netif_receive_skb_internal->__netif_receive_skb->__netif_receive_skb_core->deliver_skb->...这样的路径一路进去到某种类型的socket buffer中,这个过程跨越多个模块,跨越多个协议栈的层,甚至在极端情况下可以跨越内核态和用户态。但我们仍认为是一个线程搬移,是softirq线程把网卡上的buffer,搬移到CPU socket buffer的一个过程。调整网卡和socket buffer之间的大小,流控时间,部署给这些队列的线程的数目和调度时间,就可以调整好整个系统的性能。

从线程模型上看性能,处理模型就会比较简单:我们如果希望提高一个队列清库存的效率,只要增加在这个队列上的搬移线程,或者提高部署在上面的线程(实际上是CPU核)的数量,就可以平衡它的流控时间。如果我们能平衡所有队列的效率和长度,我们就比较容易控制整个系统的通量和时延了。

这其中当然还会涉及很多细节的设计技巧,但大方向是这个。

在线程这个问题上,最后要补充几句:线程是为了驱动系统的运行。不少新手很容易把线程和模块的概念搞到一起,甚至给每个模块配备一个线程。这是不少系统调度性能差的原因。初学者应该要时刻提醒自己,线程和模块是两个独立的,正交的概念,是不应该绑定的。模块是为了实现上的内聚,而线程是为了:

1. 把计算压力分布到多个核上

2. 匹配不同执行流程的速率

3. 当线程的执行流程中有同步IO的时候,增加线程以便减少在同步IO上的等待时间(本质上是增加流水线层次提升执行部件的同步效率)

后面我们谈具体的优化技巧的时候我们会重新提到线程的这些效果。

==一般操作方法==

所以,当我们遭遇一个通量性能瓶颈的时候,我们通常分三步来发现瓶颈的位置:

1. CPU占用率是否已经满了,这个用top就可以看到,比如下面这个例子:

有两个CPU的idle为0,另两个基本上都是接近100%的idle,我们基于这个就可以决定我们下一步的分析方向是什么了。

如果CPU没有满,有三种可能:

1.1 某个队列提早流控了。我们通过查看每个独立队列的统计来寻找这些流控的位置,决定是否需要提升队列长度来修复这种流控。例如,我们常常用ifconfig来观察网卡是否有丢包:

wlan0     Link encap:以太网  硬件地址 7c:7a:91:xx:xx:xx  
          inet 地址:192.168.0.103  广播:192.168.0.255  掩码:255.255.255.0
          inet6 地址: fe80::7e7a:91ff:fefe:5a1a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1
          接收数据包:113832 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:81183 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:1000 
          接收字节:47850861 (47.8 MB)  发送字节:18914031 (18.9 MB)

在高速网卡场景中,我们常常要修改/proc/sys中的网络参数保证收包缓冲区足够处理一波netpolling的冲击。

我们自己写业务程序的时候,也应该对各个队列的水线,丢包数等信息进行统计,这样有助于我们快速发现队列的问题。

1.2 调度没有充分展开,比如你只有一个线程,而你其实有16个核,这样就算其他核闲着,你也不能怎么样。这是需要想办法把业务hash展开到多个核上处理。上面那个top的结果就是这种情形。

1.3 配套队列的线程有IO空洞,要通过异步设计把空洞填掉,或者通过在这个队列上使用多个线程把空洞修掉。具体的原理,后面谈分析方法的时候再深入介绍。

2. 如果CPU占用率已经占满了,观察CPU的时间是否花在业务进程上,如果不是,分析产生这种问题的原因。Linux的perf工具常常可以提供良好的分析,比如这样:

这里例子中的CPU占用率已经全部占满了。但时间中只有15.43%落在主业务流程上,下面有大量的时间花在了锁和调度上。如果我们简单修改一下队列模式,我们就可以把这个占用率提升到23.39%:

当我们发现比如schedule调度特别频繁的时候,我们可以通过ftrace观察每次切换的原因,比如下面这样:

你可以看到业务线程执行3个ns就直接切换为Idle了,我们可以在业务线程上加mark看具体是什么流程导致这个切换的(如果系统真的忙,任何线程都应该用完自己的时间片,否则就是有额外的问题引起额外的代价了)

3. 如果CPU的时间确实都已经在处理业务的,剩下的问题就是看CPU执行系统是否被充分利用(比如基于例如Top Down模型分析系统CPU的执行部件是否被充分利用) 或者软件算法是否可以优化了。这个也可以通过perf和ftrace组合功能来实现。

在后面几篇博文中,我们会逐一看看Linux的perf和ftrace功能如何对这些分析提供技术支持的。


==关于流控==

流控是个很复杂的问题,这里没有准备展开,但需要补充几个值得考量的问题。

第一,流控应该出现在队列链的最前面,而不是在队列的中间。因为即使你在中间丢包了,前面几个队列已经浪费CPU时间在这些无效的包上了,这样的流控很低效。所以,中间的队列,只要可能,一般不设置自身的流控

第二,在有流控的系统上,需要注意一种很常见的陷阱:就是队列过长,导致最新的包被排到很后才处理,等完成整个系统的处理的时候,这个包已经被请求方判断超时了。这种情况会导致大量的失效包。所以,流控的开始时间要首先于每个包的超时时间。


==总结==

性能分析应该是一个有针对性的工作,我们大部分情况都不可能通过“调整这个参数看看结果,再调整调整那个参数看一个结果,然后寄望于运气。我们首先必须从一开始就建立系统的运行模型,并有意识地通过程序本身的统计以及系统的统计,对程序进行profiling,并针对性地解决问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实战Nginx:取代Apache的高性能Web服务器 作者: 张宴 出版社: 电子工业出版社 出版年: 2010年2月 内容简介   Nginx (“engine x”) 是俄罗斯人Igor Sysoev编写的一款高性能HTTP 和反向代理服务器。Nginx选择了epoll和kqueue作为网络I/O模型,在高连接并发的情况下,Nginx是Apache服务器不错的替代品,它能够支持高达50 000个并发连接数的响应,运行稳定,且内存、CPU等系统资源消耗非常低。   本书主要分为4个部分,第1部分为基础篇,介绍了Nginx服务器的安装与配置方法;第2部分为进阶篇,重点介绍了Nginx的配置优化方法、Nginx与PHP/Ruby/Python/JSP/Perl/Memcached的结合配置方法、Nginx HTTP反向代理与负载均衡的配置与优化、Nginx模块开发等,最后还分析了新浪的开源软件项目──基于Nginx的NCache网页缓存系统;第3部分为实战篇,分析了Nginx在国内知名网站(如新浪播客、搜狐博客等)中的应用案例;第4部分为模块篇,对Nginx的基本模块和第三方模块进行了集中介绍。   本书是为对配置管理Nginx服务器感兴趣的读者准备的,适用于以前没有接触过Nginx,或者对Nginx有一些了解并希望能够进一步深入学习的专业系统工程师、个人网站站长及Linux/Unix从业人员。   作者简介   张宴,就职于北京金山软件公司,金山游戏官方网站──逍遥网(xoyo.com)系统架构师,技术支持部平台组组长。曾在新浪网、赶集网等公司任系统工程师、系统架构师,工作内容主要涉及:服务器系统架构设计与部署、系统运维与调优、网络故障解决、网站后端以及接口类PHP程序开发、Unix开源软件二次开发、服务器监控系统开发、系统运维与平台研发团队管理。 第1章 Nginx简介.pdf 第2章 Nginx服务器的安装与配置.pdf 第3章 Nginx的基本配置与优化.pdf 第4章 Nginx与PHP(FastCGI)的安装、配置与优化.pdf 第5章 Nginx与JSP、ASP.NET、Perl的安装与配置.pdf 第6章 Nginx HTTP负载均衡和反向代理的配置与优化.pdf 第7章 Nginx的Rewrite规则与实例.pdf 第8章 Nginx模块开发.pdf 第9章 Nginx的Web缓存服务与新浪网的开源NCACHE模块.pdf 第10章 Nginx在国内知名网站中的应用案例.pdf 第11章 Nginx的非典型应用实例.pdf 第12章 Nginx的核心模块.pdf 第13章 Nginx的标准HTTP模块.pdf 第14章 Nginx的其他HTTP模块.pdf 第15章 Nginx的邮件模块.pdf

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值