本文主要摘自brendangregg大神的blog: http://www./blog/2018-01-17/measure-working-set-size.html 研究如何衡量一个特定业务程序的working set size。先不谈概念,让我们一起先思考一下为什么要研究WSS呢?举个例子来说,某些后台daemon为了性能,喜欢在程序在启动阶段就创建好各种fixed-size的内存池,且不支持动态grow/shrink,这些内存池的真实利用率如何?是否存在浪费?访问频率如何?读者可能会想到通过在内存池实现上做一些简单统计,不难得到程序的访问频率或者冷热内存页。那么如果是一个第三方程序呢?对测量的人来说是一个黑盒子,这种场景则只能尝试在操作系统层来考虑。本文从Linux的角度系统阐述了目前的常用手段,有什么缺陷以及一些优化手段。 作者邓刚,马涛,Linux系统工程师,来自阿里云系统组。 本文中若有任何疏漏错误,有任何建议和意见,请回复内核月谈微信公众号,或通过gavin.dg at linux.alibaba.com>或者 tao.ma at linux.alibaba.com反馈。 阿里云系统团队,是由原淘宝内核组扩建而成,2013年淘宝内核组响应阿里巴巴集团的号召,整建制转入阿里云,开始为云计算底层系统构建完善的系统支持。 阿里云系统团队是由一群具有高度使命感和自我追求的内核开发人员组成,团队中的大多数人,都是活跃的社区内核开发人员。目前的工作领域主要涉及(但不限于) Linux内核的内存管理、文件系统、网络和内核维护构建,以及和内核相关联的用户态库和工具。如果你对我们的工作很感兴趣,欢迎加入我们,请将简历发送至 tao.ma at linux.alibaba.com或者 boyu.mt at alibaba-inc.com。 正文Working Set Size(WSS)是指一个app保持正常运行所须的内存。比如一个应用在初始阶段申请了100G主存,在实际正常运行时每秒只需要50M,那么这里的50M就是一个WSS。评估WSS能干嘛呢?它可以用来进行内存容量规划并进行必要的内存扩容或者软件优化。这个问题并不新鲜而且非常重要,然而brendangregg却表示至今为止没有实际可用的有效工具(译者注:严重同意,而且在翻译这篇文章之前译者也开发了类似工具,在阿里巴巴内部使用)。有经验的读者可能会注意到Linux top命令的输出中,有两列分别是VIRT与RES,其中VIRT是进程使用的虚拟内存地址空间大小,而RES则是实际使用的物理内存(如果考虑共享映射等,则需要用到smaps proc文件的PSS或者top命令的SHARE,不过这两者对于理解WSS无益,故不在此展开)。比如,一个进程刚启动时通过私有匿名映射了(map_flags=MAP_PRIVATE | MAP_ANON)100G内存,然后实际只访问其中50G,那么VIRT=100G,RES=50G。那么问题来了,如何评估这50G内存的访存频率呢?是否存在明显的冷热区分呢?针对这个问题,brendangregg开发了两款基于Linux系统的小工具,下文将分别详细介绍。 Method 1: Referenced Page flag这是基于Linux Kernel 2.6.22引入的一个特性:the ability to set and read the referenced page flag from user space, added for analyzing memory usage. brendangregg大神基于此实现了一个https://github.com/brendangregg/wss wss.pl工具,下面的案例在0.1s内存测量mysqld(PID=423)的WSS: 上面表示0.1秒内mysqld访问了28M物理内存(总内存403.66M)。为什么选取0.1s而不是更长?大神解释说这样短时间段内的测量可以帮助我们正确评估业务程序对CPU cache的使用(比如L1/L2/L3, TLB L1/L2等)。28M略大于CPU LLC的大小,所以cache并非工作得很完美。这个工具也支持累计模式,仅重置一次referenced flag然后以固定间隔持续采集,输出如下: 可以看到WSS在逐渐增加。其中各列的含义: Est(s): 采集时刻; RSS(MB):物理内存使用(MBytes); PSS(MB):按共享映射的次数,均摊算出的物理内存使用量(MBytes); Ref(MB):在Est(s)内,进程实际访问过的内存(MBytes)。 细心的读者可能会发现,数据中的Est并非恰好是整数秒。这里的原因是因为程序本身在重置referenced flag或者从proc接口读取都需要消耗一定的时间,而且随着进程使用的内存越多,开销越大。 这个小工具的原理什么呢?对x86 MMU架构有了解的读者应该知道PTE有一个bit为 accessed bit。它的特性是:一旦CPU访问了一个内存地址,那么该地址相应的PTE的accessed bit将会被置上。这是一个硬件特性,至于如果利用它则需要我们一起发挥想象力了。著名的linux mm 子系统里的LRU页框回收算法,在除了以软件的方式打tag之外,就依赖了该bit来区分页的冷热程度。这里多说一句,其实WSS这个小工具的目的是找出特定时间段内被访问过的页,而LRU则是找出最近一段时间段内未被访问过或访问频率低的页,本质上是不是很像呢?所以大家都是用了相同的bit,做了类似的事情。回到正题,David Rientjes在2007年提了一个patchhttp://lkml./hypermail/linux/kernel/0702.1/0628.html,用于从内核导出文件/proc/PID/clear_refs,用户可以在用户态通过对特定进程清理page referenced flag,这样在/proc/PID/smaps文件中就可以查看被访问过的内存大小了。 这个工具的缺陷非常明显,比如可能影响到被测试的业务进程(比如延迟增加10%,对于100G内存的进程,影响时间超过1s),而且该工具自身也需要消耗system time。另外,该工具由于从用户态接口清理了page referenced flag,这也将影响到LRU算法的准确度,尤其是swap打开以后,很可能将一些热内存页判定为冷内存并swap到disk。更严重的是,一些老内核还有panic风险。在Linux 4.3+版本中的引入了idle page flag特性可以解决此处提到的部分缺陷,后文再详细介绍。 WSS profile chartsbrendangregg大神尤其擅长将各种性能数字图形化,本文当然也不例外。wss.pl支持profile模式(-P),以等比数列步长(即当前步长 = 上一次步长 * 2)进行统计,输出如下: 每相邻两行的采集间隔成等比递增:短采样间隔可以用来评估进程对cpu cache的利用,长采样间隔则用来评估进程的物理内存使用趋势。注意这仅是采集一轮的数据(译者注:仅在采样的初始阶段清理一次page referenced flag,可以理解为 1* write + N * read,如果读者想得到更一般化的数据,则应该采集多轮)。将上面的采集数据图形化展示: uniform 是指100M地址空间内uniform access distribution (译者注:应是指“匀速”访问100M地址空间),可以看到除第一个点外,其他点均稳定在100M,这是因为此时uniform访问的进程还没来得及访问整个地址空间。为了对比,将地址空间调整为2G,如下图所示(分别是对数坐标曲线以及线性曲线): Method 2: Idle Page FlagIdle page flag 是Vladimir Davydov在Linux4.3中实现的一个新特性,引入了两种新的page flag:idle和young,它弥补了方案一的一个重要缺陷:影响page reclaim的逻辑。该方案仍然是基于PTE的accessed bit,通过在page_ext_flags引入了额外的idle&&young page flag来保证page reclaim的逻辑不受此影响(译者注:此处关于idle page tracking实现的描述不够准确,译者理解如下:方案一的缺陷在于清理PTE的accessed bit会导致page reclaim的逻辑误判某些热页为冷页,那么通过引入一个软件上的young flag来辅助记录此页为热页,清理accessed bit时将flag置位。这样page reclaim如果遇到young flag置位的页则认为其最近同样被访问过)。另外,上述两个flag在64bit的kernel中被直接定义为page flag,在32bit的kerne中由于page flags空间不够用,则基于page extension特性来定义。以下摘自内核源码树中的文档Documentation/vm/idle_page_tracking.txt vm/idle_page_tracking.txt:
brendangregg基于此特性写了两个版本的工具,在github https://github.com/brendangregg/wss 可以获取源码,运行截图如下: 这里v1与v2在统计结果上没有差别,主要在于实现方式上:v1每次是一个page为单位进行处理,而v2是batch处理,以maps文件中的每一行(即vma)为单位进行处理,时间消耗从44s降为0.8s。(译者注:之前提到的译者自己写的工具也实现了类似的功能,采用的是v1与v2的折中方案,每次以65536个page为单位处理,不过考虑到测量进程本身的CPU开销以及内存使用,最后放弃该方案)。brendangregg还提到了如果pagemap,idlebitmap,kpagecgroup等支持mmap(2)系统调用,可以避免过多的系统调用。同时大神还想一次性把系统所有page的idle flag都设置上,这样可以得到整机的快照(译者注:idle page tracking只支持扫描LRU链表上的页,对net/kmalloc等kmem是透明的)。另外,第一次采样间隔预期是0.01s(而目前扫描一次最小消耗0.8s),为了实现这个目标,brendangregg提到可以通过发送SIGSTOP以及SIGCONT让被测量的进程停止运行,显然这很可能影响进程的正常逻辑,比如影响tcp窗口,触发超时等。(译者注:brendangregg似乎更关注于细粒度的采样,辅助性能分析;其实粗粒度采样也有参考价值,可以分析内存利用率或者集群调度等,此为后话)。 结语由于目前没有有效的工具来测量WSS,所以brendangregg大神尝试写了两个小工具。同时他还有一篇文章提到了一些其他方案来预估WSS,比如PMC,见 http://www./wss.html |
|