游戏迷提供最新游戏下载和手游攻略!

网络游戏服务器性能优化工具收集整理

发布时间:2024-09-20浏览:59

其实网络游戏服务器性能优化工具收集整理的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享网络游戏服务器性能优化工具收集整理的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!

对于这里的所有工具,你都可以通过man获取他们的帮助文档。这里我们只介绍一些常见的用法。

统计工具

虚拟机状态

我们首先看一个vmstat 的例子。使用以下命令每5 秒打印一份报告。

您可以使用ctrl+c 来停止vmstat。 vmstat的常规用法是vmstat间隔时间,即每隔interval秒采样一次,总共采样一次。如果省略times,则会收集数据,直到用户手动停止为止。

第一行值显示自系统启动以来的平均值,第二行开始显示现在正在发生的情况,接下来的行显示每5 秒间隔内发生的情况。标题中各列的含义如下:

过程

r列显示有多少进程正在等待cpu,b列显示有多少进程正在不间断地休眠(通常意味着它们正在等待IO,例如磁盘、网络、用户输入等)。

记忆

swapd 列显示有多少块已换出到磁盘(页面交换)。其余三列显示有多少块空闲(未使用)、有多少块用作缓冲区以及有多少块用作操作系统的缓存。

交换

这些列显示页面交换活动:每秒有多少块被换入(从磁盘)和换出(到磁盘)。它们比监控swpd 列重要得多。大多数时候我们期望看到si 和so 列为0,并且我们绝对不期望看到每秒超过10 个块。

io

这些列显示从块设备读取(bi) 和写入(bo) 的块数量。这通常反映硬盘I/O。

系统

这些列显示每秒的中断数(in) 和上下文切换数(cs)。除非上下文切换超过10万次甚至更多,否则一般不需要担心上下文切换。

中央处理器

这些列显示了各种操作所花费的所有CPU 时间的百分比,包括执行用户代码(非内核)、执行系统代码(内核)、空闲和等待IO。如果正在使用虚拟化,则第5 列可能是st,显示从虚拟机“窃取”的百分比。

内存不足的症状:空闲内存急剧减少,回收缓冲区和缓存无济于事,大量使用交换分区(swpd),频繁的页面交换(swap),磁盘读写次数(io)增加,页面增加故障中断(in)、上下文切换(cs)次数增加,等待IO的进程(b)增加,大量CPU时间花在等待IO(wa)上。

iostat

现在让我们转向iostat。默认情况下,它显示与vmstat 相同的CPU 使用信息。我们通常只对I/O 统计信息感兴趣,因此使用以下命令仅显示扩展设备统计信息。

与vmstat 一样,报告的第一行显示自系统启动以来的平均值(通常省略以节省空间),然后后续报告显示增量平均值,每个设备一行。

有多种选项可以显示和隐藏列。官方文档有点难以理解,因此我们必须深入研究源代码才能看到实际显示的内容。列信息描述如下:

为了了解Linux的磁盘IO指标,首先了解一些常见的缩写习惯:rq是request,r是read,w是write,qu是queue,sz是size,a是average,tm是time,svc是service 。

rrqm/s 和wrqm/s

每秒的合并读取和写入请求。 “合并”意味着操作系统从队列中取出多个逻辑请求并将它们合并为对实际磁盘的单个请求。

r/s 和w/s

每秒发送到设备的读写请求数。

rsec/s 和wsec/s

每秒读取和写入的扇区数。有些系统还输出rKB/s和wKB/s,表示每秒读写的千字节数。 (iostat -dkx 间隔时间)

avgrq-sz

请求的扇区数。 (读扇区数+写扇区数)/(读请求数+写请求数)

平均曲率

设备队列中等待的请求数。即队列的平均长度。

等待

每个IO请求所花费的时间,包括在队列中的等待时间和实际请求(服务)时间。

服务中心

实际请求(服务)时间(以毫秒为单位),不包括排队时间。

%util

至少有一个活动请求的时间百分比。更好的说法是服务时间的百分比。以上面的输出为例。一秒内读取2.5次,写入1.8次。每个请求的实际请求时间(不包括排队时间)为6.0ms,所以总共花费的时间为(2.5+1.8)*6.0ms,即25.8ms。0.0258秒,转换为百分比然后四舍五入得到util的值2.6%。

%util: 当该数字持续接近80% 以上时,您将需要采取以下任一操作- * 增加RAM,从而减少对磁盘的依赖* 增加RAID 控制器缓存,从而减少磁盘依赖* 增加磁盘数量,从而增加磁盘吞吐量(更多主轴平行工作)*水平分区

下面的公式可以计算出一个请求在整个请求期间花费了多少等待时间。当该值大于50%时,表示整个请求过程中在队列中等待的时间较多;如果这个数字很大,就应该采取相应的措施。

(await-svctim)/await*100: IO 操作在队列中等待的时间与实际接受服务的时间的百分比。如果这个数字超过50%,则每个IO 请求在队列中等待的时间比处理的时间要多。如果该比率严重向上倾斜(在75% 的范围内),您就知道您的磁盘子系统无法跟上IO 请求,并且大多数IO 请求花费大量时间在队列中等待。在这种情况下,您将需要再次执行上述任何操作

IO瓶颈症状:1.%util很高2.await比svctm大很多3.avgqu-sz比较大

下面解释iowait的作用。需要注意的是,高速CPU也可能导致iowait有较大的值。

%iowait: 该数字显示CPU 等待IO 所浪费的时间百分比。该数字的一部分可能是由网络IO 引起的,可以通过使用异步IO 库来避免。其余部分只是表明您的应用程序受IO 限制的程度。您可以通过确保磁盘IO 操作花费更少的时间、RAM 中提供更多数据、通过增加RAID 阵列中的磁盘数量来增加磁盘吞吐量、使用SSD(查看我关于固态驱动器与硬盘驱动器的文章)来减少此数字。部分数据或全部数据等

上面的解释主要参考了《高性能mysql》和这篇博客。

例子

cpu密集型机器

CPU 密集型服务器的vmstat 输出通常在us 列中具有较高值,报告非内核代码上花费的CPU 时钟;它也可能在sy 列中具有很高的值,表明系统CPU 利用率,超过20% 就足够令人不安了。大多数情况下,还会有进程队列的排队时间(在r列中报告)。这是一个例子:

如果我们查看同一台计算机上iostat 的输出(再次排除显示启动以来平均值的第一行),我们可以看到磁盘利用率低于50%:

这台机器不是IO密集型的,但是仍然有相当数量的IO发生,这在数据库服务器中很少见。另一方面,传统的Web服务器消耗大量CPU资源,但很少进行IO,因此Web服务器的输出不会像这个例子一样。

IO密集型机器

在IO 密集型工作负载下,CPU 花费大量时间等待IO 请求完成。这意味着vmstat将显示许多处理器处于不可中断睡眠(b列)状态,并且wa列中的值非常高。这是一个例子:

该机器的iostat 输出显示硬盘驱动器一直处于繁忙状态:

由于舍入误差,%util 的值可能会超过100%。有哪些迹象表明机器是IO 密集型的?只要有足够的缓冲来服务写请求,即使机器正在做大量的写操作,也可能会得到满足,但这通常意味着硬盘可能无法服务读请求。这听起来可能违反直觉,但如果你考虑一下阅读和写作的本质,似乎并非如此:

写请求可以被缓冲或同步。

读取请求本质上是同步的。当然,程序可以猜测可能需要某些数据并异步提前读取(read advance)。无论如何,通常程序必须在继续工作之前获取所需的数据。这迫使读取请求成为同步操作:程序必须阻塞,直到请求完成。

这样想:您可以向缓冲区中的某个位置发出写入请求,并在一段时间后完成它。您甚至可以每秒发出许多此类请求。如果缓冲区工作正常并且有足够的空间,则每个请求都可以快速完成,并且对物理硬盘的实际写入会更有效地重新排序和批处理。但是,对于读操作,却没有办法做到这一点———— 无论请求多小或多大,硬盘都不可能回应说:“这是你的数据,我稍后再读。”这就是为什么读取需要IO等待就可以理解了。

发生内存交换的机器

发生内存交换的机器的swpd 列中可能有也可能没有高值。但您可以看到si 和so 列具有非常高的值,这是我们不希望看到的。以下是内存交换严重的机器的vmstat 输出:

闲置机器

以下是空闲计算机上的vmstat 输出。可以看到idle一栏显示CPU处于100%空闲状态。

数据统计

dstat显示CPU使用率、磁盘IO状态、网络包发送状态和分页状态。

个人感觉iostat和vmstat的输出虽然详细,但是不够直观,不如dstat那么好用。此外,dstat 输出带有颜色以提高可读性。现在dstat 是我检查系统状态的首选工具。

使用dstat时,直接输入命令即可,不带任何参数。您还可以通过指定参数来显示更详细的信息。

dstat-cdmpsy

PID统计

系统IO的大部分信息都是通过iostat获取的。这个粒度只能精确到每个设备。通常我们想知道每个进程在线程级别发起了多少个IO。在Linux 2.6.20之前,除了使用systemtap之类的工具之外,没有其他办法可以实现这一点,因为系统没有公开这方面的统计信息。您现在可以使用一个名为pidstat 的工具,其使用方法如下:

pidstat-dinterval

另外,pidstat还可以用来统计CPU的使用信息。

pidstat-uinterval

统计内存信息:

pidstat -r 间隔

统计数据

mpstat用于统计多核处理器中每个处理器的使用情况。 mpstat的使用非常简单。常见用法如下:

mpstat -P ALL 间隔时间

毫不奇怪,执行上述命令后读者无法弄清楚原因。尝试运行以下命令,同时查看mpstat 的输出。

sysbench --test=cpu --cpu-max-prime=20000 运行

注:sysbench是一个基准测试工具,可以用来测试cpu、io、mutex、oltp等。在Debain系统下,通过以下命令安装:

sudo apt-get install sysbench

网络统计

Netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据。一般用于检查机器各端口的网络连接情况。

对于我自己来说,最常见的用法如下:

netstat-npl

上面的命令可以检查你要打开的端口是否已经打开,可以作为程序是否已经启动的依据。

网络统计-rn

其中,flag字段的解释如下:

U 该路线可用

G 该路由是到网关(路由器)的。如果未设置该标志,则直接连接目标地址。

H 路由到主机

D 路由由重定向消息创建

M 路由已被重定向消息修改

netstat还可以提供系统的接口信息:

网络统计输入

此命令打印每个接口的MTU、输入数据包计数、输入错误、输出数据包计数、输出错误、冲突和当前输出队列长度。

顶级工具

顶部

top命令的摘要区域显示了系统性能信息的五个方面: 1.负载:时间、登录用户数、平均系统负载2.进程:运行、睡眠、停止、僵尸3.CPU:用户态、核心模式、NICE、空闲、等待IO、中断等4、内存:总量、已用、空闲(系统视角)、缓冲区、缓存5、交换分区:总量、已用、空闲

任务区默认显示:进程ID、有效用户、进程优先级、NICE值、进程使用的虚拟内存、物理内存和共享内存、进程状态、CPU使用率、内存使用率、累计CPU时间、进程命令行信息。

查看某个进程所有线程的CPU使用率:

顶部-H -p [PID]

顶部

另一个更好的top 替代品是htop。 htop 是Linux 系统中的交互式进程查看器,是需要ncurses 的文本模式应用程序(在控制台或X 终端中)。

与Linux传统的top相比,htop更加人性化。它允许用户交互操作,支持颜色主题,可以水平或垂直滚动进程列表,并支持鼠标操作。

与top相比,htop有以下优点:

您可以水平或垂直滚动进程列表以查看所有进程和完整的命令行。

启动方面,比top要快。

杀死进程时不需要输入进程ID。

htop支持鼠标操作。

参考:htop,Linux中替代top的进程管理工具

奥托普

通过iostat和dstat我们可以知道系统当前的IO负载,但是IO负载具体是哪个进程产生的呢?这时候我们需要的是iotop。

iotop 是一个用于监控磁盘I/O 使用情况的顶级工具。它有一个和top类似的UI,包括PID、用户、I/O、进程等相关信息。 iotop 使用Python 语言编写,需要Python 2.5(及更高版本)和Linux 内核2.6.20(及更高版本)。使用起来非常简单,这里就不做过多介绍了。详细信息请参考官网:http://guichaz.free.fr/iotop/

该命令也可以非交互方式使用:

iotop-bod 间隔

要查看各个进程的IO,还可以使用pidstat命令。与iotop 不同,pidstat 不需要root 权限。

pidstat-dinterval

聚苯乙烯

最常见的用法

ps aux #BSD ps -ef #linux

ps参数太多。具体使用方法请man ps。以下是两种比较常见的使用方法。

一种终止程序的方法。

辅助| mysqld | grep mysqld | grep -v grep | grep -v awk '{ 打印$2 }' | xargs杀-9

杀死僵尸进程:

ps-eal | awk '{ if ($2=='Z' ){ print $4}}' | xargs杀-9

追踪工具

斯特雷斯

我们在编写程序的时候,会调用很多库函数,而这些库函数只是系统调用的封装。它们最终会调用操作系统提供的系统调用,通过系统调用访问硬件设备。 strace的作用就是将这些调用关系显示出来,让程序员知道一个函数被调用时到底做了什么事情。当然,strace远比上面的介绍更加灵活和通用。

让我们看一个例子:

要检查mysqld 在Linux 上加载哪个配置文件,可以运行以下命令行:

strace -e stat64 mysqld --print-defaults /dev/null

时间工具

正常运行时间

Uptime是最简单、最简单的工具,但也有一个值得讨论的地方,那就是它输出的最后三个数字是怎么得到的,又代表什么意思?

这三个数字的含义分别是系统在1分钟、5分钟、15分钟内的平均负载。关于这些数字的含义和来历,建议阅读阮一峰的文章《理解linux系统负荷这里》。

我想说的是,我每天、每时每刻都在看着这三个数字,但我从来不相信它们。

这句话怎么说呢?我之所以每天、一直看,是因为我将这三个数字显示在tmux 的状态栏上,这样我就可以随时看到它们,而无需专门输入uptime 命令。

我为什么不相信呢?因为这些数字只能显示有多少线程正在等待CPU。如果我们的一个任务有很多线程,那么系统负载并不是特别高,但是这些数字会高得惊人,也就是不能完全相信正常运行时间的原因。不信的话可以执行下面的语句,然后看uptime的输出。

sysbench --test=mutex --num-threads=1600 --mutex-num=2048 \ --mutex-locks=1000000 --mutex-loops=5000 运行

运行5分钟后,我的电脑上输出如下。需要强调的是,此时电脑完全没有卡顿。通过查看正常运行时间来判断系统负载与通过听CPU风扇的声音来判断系统负载是一样的。只能作为线索,不能作为系统负载高的依据。

20:32:39 最多10:21, 4 个用户, 平均负载: 386.53, 965.37, 418.57

其他

拉索夫

lsof(列出打开的文件)是一个列出当前系统上打开的文件的工具。在Linux环境中,一切都以文件的形式存在。通过文件,您不仅可以访问常规数据,还可以访问网络连接和硬件。因此,例如传输控制协议(TCP)和用户数据报协议(UDP)套接字,系统会在后台为应用程序分配文件描述符。无论文件的性质如何,应用程序都会使用文件描述符。提供与底层操作系统交互的通用接口。由于应用程序打开的文件的描述符列表提供了有关应用程序本身的大量信息,因此能够通过lsof 工具查看此列表对于系统监控和故障排除很有帮助。

如何使用lsof 可以在这里找到。这里只是一些常见用途。

检查阻止lsof /boot 的文件系统

查看端口号被哪个进程占用lsof -i :3306

检查用户打开了哪些文件lsof -u username

检查进程打开了哪些文件lsof -p 4838

查看远程打开的网络链接lsof -i @192.168.34.128

分析工具Perf简介

介绍

Perf是一款基于Linux 2.6+系统的分析工具。它抽象了Linux 中性能测量中CPU 的硬件差异,并提供了简单的命令行界面。 Perf 基于最新版本Linux 内核的perf_events 接口。本文通过示例演示了Perf工具的使用。输出是在使用双核Intel Core2 T7100 CPU 的HP 6710 b 上的Ubuntu 11.04(内核版本2.6.38-8-generic)上获得的。为了便于阅读,某些输出使用省略号([.])。

命令

Perf 工具提供了一组丰富的命令来收集和分析性能和跟踪数据。命令行的用法和git类似。通过一个通用命令Perf,一组子命令: stat、record、report,[.]

支持的命令列表:

perfusage: perf [--version] [--help] 命令[ARGS] 最常用的perf 命令是:annotate 读取perf.data(由perf 记录创建)并显示带注释的codearchive 使用在perf 中找到的构建ID 的目标文件创建存档。 data filebench 基准测试套件的通用框架buildid-cache 管理build-idcache.buildid-list 列出perf.data 中的buildidiff 读取两个perf.data 文件并显示差异配置文件inject 过滤器以使用附加信息扩充事件流kmem 跟踪/测量工具内核内存(slab) 属性kvm 跟踪/测量kvm guest 的工具oslist 列出所有符号事件类型lock 分析锁定事件probe 定义新的动态跟踪点record 运行命令并将其配置文件记录到perf.datareport 读取perf.data(由perf 记录创建)并显示profileched 跟踪/测量调度程序属性(延迟)的工具script 读取perf.data(由perf 记录创建)并显示跟踪输出stat 运行命令并收集性能计数器统计信息test 运行健全性测试.timechart 在工作负载期间可视化总体系统行为的工具top 系统分析有关特定命令的更多信息,请参阅“perf help COMMAND”。

某些需要特定内核支持的命令可能无法运行。如果您想要每个子命令的特定选项列表,只需输入命令名称,后跟-h:

perf stat -husage: perf stat [] []-e, --eventevent 选择器。使用'perf list' 列出可用事件-i, --no-inherit 子任务不继承计数器-p, --pidstat 现有进程id-t 上的事件, --tidstat 现有线程id-a, --来自al 的所有cpu 系统范围的集合

l CPUs-c, --scale scale/normalize counters-v, --verbose be more verbose (show counter open errors, etc)-r, --repeatrepeat command and print average + stddev (max: 100)-n, --null null run - dont start any counters-B, --big-num print large numbers with thousands' separators 事件 perf_events接口还提供了一组通用的的硬件事件名称。在每个处理器,这些事件被映射到一个CPU的真实事件,如果真实事件不存在则事件不能使用。可能会让人混淆,这些事件也被称为硬件事件或硬件缓存事件 。 最后,还有由内核ftrace基础实现的tracepoint事件。但只有2.6.3x和更新版本的内核才提供这些功能。 可以通过命令获得可支持的事件列表: perf listList of pre-defined events (to be used in -e):cpu-cycles OR cycles [Hardware event]instructions [Hardware event]cache-references [Hardware event]cache-misses [Hardware event]branch-instructions OR branches [Hardware event]branch-misses [Hardware event]bus-cycles [Hardware event]cpu-clock [Software event]task-clock [Software event]page-faults OR faults [Software event]minor-faults [Software event]major-faults [Software event]context-switches OR cs [Software event]cpu-migrations OR migrations [Software event]alignment-faults [Software event]emulation-faults [Software event]L1-dcache-loads [Hardware cache event]L1-dcache-load-misses [Hardware cache event]L1-dcache-stores [Hardware cache event]L1-dcache-store-misses [Hardware cache event]L1-dcache-prefetches [Hardware cache event]L1-dcache-prefetch-misses [Hardware cache event]L1-icache-loads [Hardware cache event]L1-icache-load-misses [Hardware cache event]L1-icache-prefetches [Hardware cache event]L1-icache-prefetch-misses [Hardware cache event]LLC-loads [Hardware cache event]LLC-load-misses [Hardware cache event]LLC-stores [Hardware cache event]LLC-store-misses [Hardware cache event]LLC-prefetch-misses [Hardware cache event]dTLB-loads [Hardware cache event]dTLB-load-misses [Hardware cache event]dTLB-stores [Hardware cache event]dTLB-store-misses [Hardware cache event]dTLB-prefetches [Hardware cache event]dTLB-prefetch-misses [Hardware cache event]iTLB-loads [Hardware cache event]iTLB-load-misses [Hardware cache event]branch-loads [Hardware cache event]branch-load-misses [Hardware cache event]rNNN (see 'perf list --help' on how to encode it) [Raw hardware event descriptor]mem:[:access] [Hardware breakpoint]kvmmmu:kvm_mmu_pagetable_walk [Tracepoint event][...]sched:sched_stat_runtime [Tracepoint event]sched:sched_pi_setprio [Tracepoint event]syscalls:sys_enter_socket [Tracepoint event]syscalls:sys_exit_socket [Tracepoint event][...] 一个事件可以有子事件(或掩码)。 在某些处理器上的某些事件,可以组合掩码,并在其中一个子事件发生时进行测量。最后,一个事件还可以有修饰符,也就是说,通过过滤器可以改变事件被计数的时间或方式。 硬件事件 PMU硬件事件取决与特定的CPU,由CPU供应商提供文档。如果将Perf工具与libpfm4库链接,则可以提供事件的一些简短描述。有关Intel和AMD处理器的PMU硬件事件的列表,请参阅 英特尔PMU事件表:手册的附录A 在这里 AMD PMU事件表:手册的3.14节 在这里 使用perf stat进行统计 对于任何支持的事件,Perf可以在进程运行期间持续计数。 在统计模式下,在应用程序运行结束时事件的发生会被简单地汇总并显示在标准输出上。去产生这些统计数据,使用 stat命令的Perf。 例如: perf stat -B dd if=/dev/zero of=/dev/null count=10000001000000+0 records in1000000+0 records out512000000 bytes (512 MB) copied, 0.956217 s, 535 MB/sPerformance counter stats for 'dd if=/dev/zero of=/dev/null count=1000000':5,099 cache-misses # 0.005 M/sec (scaled from 66.58%)235,384 cache-references # 0.246 M/sec (scaled from 66.56%)9,281,660 branch-misses # 3.858 % (scaled from 33.50%)240,609,766 branches # 251.559 M/sec (scaled from 33.66%)1,403,561,257 instructions # 0.679 IPC (scaled from 50.23%)2,066,201,729 cycles # 2160.227 M/sec (scaled from 66.67%)217 page-faults # 0.000 M/sec3 CPU-migrations # 0.000 M/sec83 context-switches # 0.000 M/sec956.474238 task-clock-msecs # 0.999 CPUs0.957617512 seconds time elapsed 如果没有指定事件,perf stat会收集上面列出的常见事件。一些是软事件例如context-switches,另一些是通用硬件事件例如cycles。在哈希符号之后,可以显示衍生指标,例如“ IPC”(每个周期的指令)。。 事件控制选项 Perf工具可以测量的一个或多个事件。事件使用其符号名称指定,后可选跟随掩码和修饰符。事件名称、掩码和修饰符不区分大小写。 默认情况下,事件同时测量用户和内核级别: perf stat -e cycles dd if=/dev/zero of=/dev/null count=100000 如果测量仅在用户级别,有增加一个修饰词: perf stat -e cycles:u dd if=/dev/zero of=/dev/null count=100000 同时测量用户和内核(显式): perf stat -e cycles:uk dd if=/dev/zero of=/dev/null count=100000 修饰符 事件可以通过冒号添加一个或多个修饰符。 修饰符允许用户对事件计数进行限制。 测量PMU事件,通过下示修饰符: perf stat -e instructions:u dd if=/dev/zero of=/dev/null count=100000 在这个例子中,我们测量用户级别的指令数量。 注意,对于真实事件,修饰符取决于底层的PMU模型。 修饰符可以随意组合。 这张简单的表格,总结了用于Intel和AMD x86处理器的最常见的修饰符。 修饰符 描述 例子 u priv 3,2,1级别监控(用户) event:u k priv 0级别监控(内核) event:k h 在虚拟化环境中监视监控程序事件 event:h H 在虚拟化环境中监视主机 event:H G 在虚拟化环境中监视访客机 event:G 以上所有修饰符均视为布尔值(标志)。 硬件事件 要测量硬件供应商文档提供的实际PMU,请传递十六进制参数代码: perf stat -e r1a8 -a sleep 1Performance counter stats for 'sleep 1':210,140 raw 0x1a81.001213705 seconds time elapsed 多个事件 要测量多个事件,只需提供一个用逗号分隔的列表,其中没有空格: perf stat -e cycles,instructions,cache-misses [...] 然而,如果每个事件使用一个文件描述符,在per-thread(per-thread模式)或per-cpu(系统范围)模式下,则可能达到内核限制的每个进程的最大文件描述符数。在这种情况下,perf将报告一个错误。有关此问题的帮助,请参阅故障排除部分。 事件的多路复用和缩放 如果事件多于计数器,则内核会使用时间多路复用(开关频率= HZ,通常为100或1000)为每个事件提供访问监视硬件的机会。复用仅适用于PMU事件。使用多路复用时,不会一直测量事件。运行结束时,该工具会根据启用的总时间与运行时间来缩放计数。实际公式为: final_count = raw_count * time_enabled / time_running 如果在整个运行过程中都对事件进行了测量,则可以估算该计数是多少。理解这是一个估计值而不是实际计数非常重要。工作负载较重时会有测量丢失,这种情况会在缩放时引入错误。 目前事件以循环方式进行管理,因此每个事件最终都将有机会运行。如果有N个计数器,则循环列表中最多前N个事件被编程到PMU中。在某些情况下它可能小于该值,因为某些事件可能无法一起测量或与它们使用同一计数器。此外,perf_events接口允许多个工具同时测量同一线程或CPU。每个事件都添加到相同的循环队列中。不能保证工具的所有事件都顺序存储在队列中。 为了避免缩放(在只有一个活动perf_event用户),你可以试着减少事件的数量。下表为一些常见的处理器提供计数器的数量: 处理器 通用的计数器 固定的计数器 英特尔酷睿 英特尔Nehalem 通用计数器可以测量任何事件,固定计数器只能测量一个事件。一些计数器可能是用于特殊用途,如看门狗定时器。 下面的例子显示缩放的影响: perf stat -B -e cycles,cycles ./noploop 1Performance counter stats for './noploop 1':2,812,305,464 cycles2,812,304,340 cycles1.302481065 seconds time elapsed 在这里,没有多路复用,因此没有缩放。 让我们再添加一个事件: perf stat -B -e cycles,cycles,cycles ./noploop 1Performance counter stats for './noploop 1':2,809,725,593 cycles (scaled from 74.98%)2,810,797,044 cycles (scaled from 74.97%)2,809,315,647 cycles (scaled from 75.09%)1.295007067 seconds time elapsed 有多路复用,从而进行缩放。尝试保始终将事件A和B一起测量的方式非常有趣。尽管perf_events内核接口提供了对事件分组的支持,但当前的Perf工具没有。 重复测量 可以使用perf stat多次运行相同的测试工作,并针对每个计数获取均值的标准偏差。 perf stat -r 5 sleep 1Performance counter stats for 'sleep 1' (5 runs):cache-misses20,676 cache-references # 13.046 M/sec ( +- 0.658% )6,229 branch-misses # 0.000 % ( +- 40.825% )branchesinstructionscycles144 page-faults # 0.091 M/sec ( +- 0.139% )0 CPU-migrations # 0.000 M/sec ( +- -nan% )1 context-switches # 0.001 M/sec ( +- 0.000% )1.584872 task-clock-msecs # 0.002 CPUs ( +- 12.480% )1.002251432 seconds time elapsed ( +- 0.025% ) 在这里,sleep运行5次,并打印每个事件的平均计数以及std-dev/mean的比率。 环境控制选项 Perf工具可用于统计每个线程、每个进程、每个cpu或整个系统的事件。在per-thread模式下,计数器只监视指定线程的执行。当线程被调度出时,监视将停止。当线程从一个处理器迁移到另一个处理器时,计数器在当前处理器上保存,并在新处理器上还原。 per-process模式是per-thread的一个变体,进程中的所有 线程都被监控。计数和采样在进程级别被合计。 perf_events接口允许自动继承fork ()和pthread_create ()。 默认情况下,Perf工具使能继承。 在per-cpu的模式下,指定处理器上所有线程都被监控。计数和采样在每个CPU上合计。一个事件一次只能监视一个CPU。如果元跨多个处理器进行监控,则需要创建多个事件。Perf工具可以统计跨多个处理器计数和采样。它也可以只监视一个部分处理器。 计数和继承 默认情况下, perf stat统计进程的所有线程和后续的子进程和线程。这可以使用 -i选项进行切换。但它无法获得per-thread和per-process的详细计数。 Processor-wide模式 默认情况下, perf stat使用per-thread计数模式。 要按per-cpu计算,请使用-a选项。当选项被设置时,所有在线处理器都会被监视,并且计数会被合计。例如: perf stat -B -ecycles:u,instructions:u -a dd if=/dev/zero of=/dev/null count=20000002000000+0 records in2000000+0 records out1024000000 bytes (1.0 GB) copied, 1.91559 s, 535 MB/sPerformance counter stats for 'dd if=/dev/zero of=/dev/null count=2000000':1,993,541,603 cycles764,086,803 instructions # 0.383 IPC1.916930613 seconds time elapsed 测量收集了所有CPU上的事件周期和指令。测量的持续时间由dd的执行决定。换句话说,此测量捕获dd进程的执行以及所有cpu上在用户级别以外运行的任何内容。 若要计时测量的持续时间而不消耗周期,可以使用/usr/bin/sleep命令: perf stat -B -ecycles:u,instructions:u -a sleep 5Performance counter stats for 'sleep 5':766,271,289 cycles596,796,091 instructions # 0.779 IPC5.001191353 seconds time elapsed 可以使用-C选项限制监视cpu的一个子集。可以传递要监视的CPU列表。例如,要在CPU0、CPU2和CPU3上测量: perf stat -B -e cycles:u,instructions:u -a -C 0,2-3 sleep 5 演示机只有两个CPU,但我们可以限制为CPU 1。 perf stat -B -e cycles:u,instructions:u -a -C 1 sleep 5Performance counter stats for 'sleep 5':301,141,166 cycles225,595,284 instructions # 0.749 IPC5.002125198 seconds time elapsed 计数在所有被监视的CPU上合计。注意,当测量单个CPU时,计数的周期和指令的数量是减半的。 连接到一个正在运行的进程 可以使用Perf连接到已经运行的线程或进程。 这需要具有附加线程或进程ID的权限。若要附加到进程,-p选项必须是进程ID。若要附加到通常在许多Linux计算机上运行的sshd服务,使用: ps ax | fgrep sshd2262 ? Ss 0:00 /usr/sbin/sshd -D2787 pts/0 S+ 0:00 fgrep --color=auto sshdperf stat -e cycles -p 2262 sleep 2Performance counter stats for process id '2262':cycles2.001263149 seconds time elapsed 决定测量持续时间的是要执行的命令。即使我们附加到进程,我们仍然可以传递命令的名称。它用于计算测量时间。没有它,Perf将一直监视,直到它被杀死。还要注意,附加到进程时,将监视该进程的所有线程。此外,假设继承在默认情况下处于打开状态,子进程或线程也将被监视。要关闭此功能,必须使用-i选项。可以在进程中附加特定线程。所谓线程,我们指的是内核可见线程。换句话说,通过ps或top命令可见的线程。要附加到线程,必须使用-t选项。我们看一下rsyslogd,因为它总是在Ubuntu 11.04上运行,有多个线程。 ps -L ax | fgrep rsyslogd | head -5889 889 ? Sl 0:00 rsyslogd -c4889 932 ? Sl 0:00 rsyslogd -c4889 933 ? Sl 0:00 rsyslogd -c42796 2796 pts/0 S+ 0:00 fgrep --color=auto rsyslogdperf stat -e cycles -t 932 sleep 2Performance counter stats for thread id '932':cycles2.001037289 seconds time elapsed 在本例中,线程932在测量的2s期间没有运行。否则,我们将看到一个计数值。附加到内核线程是可能的,但实际上并不推荐这样做。考虑到内核线程倾向于固定到特定的CPU,最好使用cpu-wide模式。 控制输出选项 perf stat可以修改输出以满足不同的需求。 大数字输出 对大多数人来说,很难读懂很大的数字。使用perf stat,可以使用逗号分隔符打印数千个大数字(美式)。为此,必须设置-B选项和设置正确的语言环境LC_NUMERIC。如上面的例子所示,Ubuntu已经正确地设置了语言环境信息。显式调用如下所示: LC_NUMERIC=en_US.UTF8 perf stat -B -e cycles:u,instructions:u dd if=/dev/zero of=/dev/null count=10000000100000+0 records in100000+0 records out51200000 bytes (51 MB) copied, 0.0971547 s, 527 MB/sPerformance counter stats for 'dd if=/dev/zero of=/dev/null count=100000':96,551,461 cycles38,176,009 instructions # 0.395 IPC0.098556460 seconds time elapsed 机器可读的输出 perf stat还可以打印计数,格式可以很容易地导入到电子表格或由脚本进行解析。-x选项改变输出的格式,并允许用户传递分隔符。这使得很容易生成CSV样式的输出: perf stat -x, dateThu May 26 21:11:07 EDT 2011884,cache-misses32559,cache-references,branch-misses,branches,instructions,cycles188,page-faults2,CPU-migrations0,context-switches2.350642,task-clock-msecs 请注意,选项-x与-B不兼容。 使用Perf记录采样 perf工具可用于收集per-thread、per-process和per-cpu的性能数据。 有几个与采样相关的命令:record、report、annotate。必须首先使用perf record收集样本。这将生成一个名为perf.data的输出文件。然后,可以使用perf report和perf annotate命令分析该文件(可能在另一台计算机上)。该方式类似于OProfile。 基于事件的采样 Perf_events基于基于事件的采样。周期表示为事件发生的次数,而不是计时器计时的次数。当采样计数器溢出时,即从2^64换回0时,记录采样。PMU没有实现64位硬件计数器,但perf_events在软件中模拟该计数器。 perf_events模拟64位计数器的方式仅限于使用实际硬件计数器中的位数来表示采样周期。在小于64位的情况下,内核会自动截断周期。因此,如果在32位系统上运行,最好周期始终小于2^31。 在计数器溢出时,内核记录有关程序执行的信息,也就是采样。记录的内容取决于测量的类型。这都是由使用者和工具指定的。但一般来说,样本中的关键信息是指令指针,即时程序中断在哪里。 基于中断的采样在现代处理器上引入了skid。这意味着每个采样的指令指针指向程序处理PMU中断的位置,而不是计数器实际溢出的位置,即它在采样周期结束时的位置。在某些情况下,如果有分支,这两个点之间的距离可能是几十条或更多的指令。当程序不再向前运行时,这两个位置确实是相同的。因此,在解释分析数据时必须小心。 默认事件:时钟周期计数 默认情况下, perf record使用时钟周期事件做为抽样事件。这是由内核映射到特定PMU事件的一个通用的硬件事件。对于英特尔来说,映射到 UNHALTED_CORE_CYCLES。在CPU频率扩展的情况下,此事件在时间上不能保持恒定不变。英特尔提供了另一个名为UNHALTED_REFERENCE_CYCLES的事件,但此事件当前不适用于perf_events。 在AMD系统中,事件映射到 CPU_CLK_UNHALTED事件,这个事件也受到频率扩展的影响。 在任何英特尔或AMD处理器,周期事件在处理器空闲时不计数,例如当它调用 mwait ()。 时间和速度 perf_events接口允许两种模式表达采样周期: 事件发生的次数(时间) 样本/秒的平均速率(频率) Perf工具默认使用平均速率。它设置为1000 hz,或1000样本/秒。 这意味着内核会动态调整采样周期以达到目标平均速率。周期内的调整会在原始的分析数据中报告。与此相反,与其他模式相比,采样周期由用户设置,并且在采样之间不发生变化。目前不支持随机采样周期。 收集样本 默认情况下,perf record在运行在per-thread模式下,并且开始继承模式。当执行一个繁忙循环的简单程序时,简单的使用如下: perf record ./noploop 1[ perf record: Woken up 1 times to write data ][ perf record: Captured and wrote 0.002 MB perf.data (~89 samples) ] 上面的示例以1000Hz的平均目标速率收集事件周期的样本。生成的示例将保存到perf.data文件中。如果文件已经存在,可能会提示您通过-F覆盖它。要将结果放入特定文件中,请使用-o选项。

警告:报告的样本数只是估计值。它没有反映实际采集的样本数量。此估计基于写入perf.data文件的字节数和最小样本大小。但每个样本的真正大小取决于测量的类型。一些样本由计数器本身生成,而另一些样本则与后处理期间支持符号相关,例如mmap()信息。 要获取perf.data文件的准确样本数,可以使用perf report命令: perf record ./noploop 1[ perf record: Woken up 1 times to write data ][ perf record: Captured and wrote 0.058 MB perf.data (~2526 samples) ]perf report -D -i perf.data | fgrep RECORD_SAMPLE | wc -l1280 指可使用 -F选项自定义采样速度。 例如,仅在用户级别对事件指令进行采样,并且使用250个样本/秒的平均速率: at an average rate of 250 samples/sec:perf record -e instructions:u -F 250 ./noploop 4[ perf record: Woken up 1 times to write data ][ perf record: Captured and wrote 0.049 MB perf.data (~2160 samples) ] 要指定采样周期,可以使用-c选项。例如,仅在用户级别收集每2000次事件指令的采样,请执行以下操作: perf record -e retired_instructions:u -c 2000 ./noploop 4[ perf record: Woken up 55 times to write data ][ perf record: Captured and wrote 13.514 MB perf.data (~590431 samples) ] Processor-wide模式 在per-cpu模式下,收集受监控cpu上执行的所有线程的样本。要在per-cpu模式下切换perf record,需要使用-a选项。默认情况下,在此模式下,所有联机CPU都被监视。正如上面perf stat所解释的,可以使用-C选项限制到CPU的一个子集。 要在所有CPU上以1000个样本/秒的平均目标速率对用户和内核级别的周期采样5秒: perf record -a -F 1000 sleep 5[ perf record: Woken up 1 times to write data ][ perf record: Captured and wrote 0.523 MB perf.data (~22870 samples) ] 使用perf report分析采样 perf record收集的样本会保存到一个二进制文件中,默认情况下,该文件名为perf.data。perf report命令读取此文件并生成简明的执行概要文件。默认情况下,样本按函数排序,样本数最多的优先。可以自定义排序顺序,从而以不同的方式查看数据。 perf report# Events: 1K cycles## Overhead Command Shared Object Symbol# ........ ............... .............................. .....................................#28.15% firefox-bin libxul.so [.] 0xd10b454.45% swapper [kernel.kallsyms] [k] mwait_idle_with_hints4.26% swapper [kernel.kallsyms] [k] read_hpet2.13% firefox-bin firefox-bin [.] 0x1e3d1.40% unity-panel-ser libglib-2.0.so.0.2800.6 [.] 0x886f1[...] 最后一列显示了符号名称。 样本可以使用多种方式进行呈现,即使用排序。例如按共享对象进行排序,使用dso: perf report --sort=dso# Events: 1K cycles## Overhead Shared Object# ........ ..............................#38.08% [kernel.kallsyms]28.23% libxul.so3.97% libglib-2.0.so.0.2800.63.72% libc-2.13.so3.46% libpthread-2.13.so2.13% firefox-bin1.51% libdrm_intel.so.1.0.01.38% dbus-daemon1.36% [drm][...] 输出控制选项 为使输出更容易解析,可以修改列分隔符为某一个字符: perf report -t 内核报告控制选项 Perf工具不知道如何从压缩内核映像(vmlinuz)中提取符号。因此,用户必须将非压缩内核镜像的路径通过 -k传递给Perf: perf report -k /tmp/vmlinux 当然,内核镜像只有带debug符号编译的才能工作。 Processor-wide模式 在per-cpu的模式中,会从监控CPU上的所有线程上记录的样本。 这样,我们可以收集来自许多不同进程的样本。例如,如果我们监视所有CPU 5s: perf record -a sleep 5perf report# Events: 354 cycles## Overhead Command Shared Object Symbol# ........ ............... .......................... ......................................#13.20% swapper [kernel.kallsyms] [k] read_hpet7.53% swapper [kernel.kallsyms] [k] mwait_idle_with_hints4.40% perf_2.6.38-8 [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore4.07% perf_2.6.38-8 perf_2.6.38-8 [.] 0x34e1b3.88% perf_2.6.38-8 [kernel.kallsyms] [k] format_decode[...] 当符号打印为十六进制地址时,这是因为ELF镜像没有符号表。当二进制文件被剥离时就会发生这种情况。我们也可以按cpu排序。这可能有助于确定工作负载是否平衡: perf report --sort=cpu# Events: 354 cycles## Overhead CPU# ........ ...#65.85% 134.15% 0 计算开销 perf收集调用链时,开销可以在两列中显示为“Children”和“Self”。“self”开销只是通过所有入口(通常是一个函数,也就是符号)的所有周期值相加来计算的。这是perf传统显示方式,所有“self”开销值之和应为100%。 “children”开销是通过将子函数的所有周期值相加来计算的,这样它就可以显示更高级别函数的总开销,即使它们不直接参与更多的执行。这里的“Children”表示从另一个(父)函数调用的函数。 所有“children”开销值之和超过100%可能会令人困惑,因为它们中的每一个已经是其子函数的“self”开销的累积。但是如果启用了这个功能,用户可以找到哪一个函数的开销最大,即使样本分布在子函数上。 考虑下面的例子,有三个函数如下所示。 void foo(void) {/* do something */}void bar(void) {/* do something */foo();}int main(void) {bar()return 0;} 在本例中,“foo”是“bar”的子级,“bar”是“main”的直接子级,因此“foo”也是“main”的子级。换句话说,“main”是“foo”和“bar”的父级,“bar”是“foo”的父级。 假设所有样本都只记录在“foo”和“bar”中。当使用调用链记录时,输出将在perf report的常规(仅自开销)输出中显示如下内容: Overhead Symbol........ .....................60.00% foo|--- foobarmain__libc_start_main40.00% bar|--- barmain__libc_start_main 启用--children选项时,子函数(即'foo'和'bar')的'self'开销值将添加到父函数中,以计算'children'开销。在这种情况下,报告可以显示为: Children Self Symbol........ ........ ....................100.00% 0.00% __libc_start_main|--- __libc_start_main100.00% 0.00% main|--- main__libc_start_main100.00% 40.00% bar|--- barmain__libc_start_main60.00% 60.00% foo|--- foobarmain__libc_start_main 在上述输出中,“foo”的“self”开销(60%)被添加到“bar”、“main”和“__libc_start_main”的“children”开销中。同样,“bar”的“self”开销(40%)添加到“main”和“libc”的“children”开销中。 因此,首先显示'__libc_start_main'和'main',因为它们有相同(100%)的“子”开销(即使它们没有“自”开销),并且它们是'foo'和'bar'的父级。 从v3.16开始,默认情况下会显示“children”开销,并按其值对输出进行排序。通过在命令行上指定--no-children选项或在perf配置文件中添加“report.children=false”或“top.children=false”,禁用“children”开销。 使用perf annotate分析源码 可以使用perf annotate深入到指令级分析。为此,需要使用要解析的命令的名称调用perf annotate。所有带样本的函数都将被反汇编,每条指令都将报告其样本的相对百分比: perf record ./noploop 5perf annotate -d ./noploop------------------------------------------------Percent | Source code & Disassembly of noploop.noggdb------------------------------------------------:::: Disassembly of section .text::: 08048484:0.00 : 8048484: 55 push %ebp0.00 : 8048485: 89 e5 mov %esp,%ebp[...]0.00 : 8048530: eb 0b jmp 804853d15.08 : 8048532: 8b 44 24 2c mov 0x2c(%esp),%eax0.00 : 8048536: 83 c0 01 add $0x1,%eax14.52 : 8048539: 89 44 24 2c mov %eax,0x2c(%esp)14.27 : 804853d: 8b 44 24 2c mov 0x2c(%esp),%eax56.13 : 8048541: 3d ff e0 f5 05 cmp $0x5f5e0ff,%eax0.00 : 8048546: 76 ea jbe 8048532[...] 第一列报告在该指令在捕获函数==noploop()==的样本百分比。如前所述,您应该仔细解读这些信息。 如果使用-ggdb编译应用程序,perf annotate可以生成源代码级信息。下面的代码片段显示了在使用此调试信息编译noploop时,同一次执行noploop时的更多信息输出。 ------------------------------------------------Percent | Source code & Disassembly of noploop------------------------------------------------:::: Disassembly of section .text::: 08048484:: #include: #include: #include:: int main(int argc, char **argv): {0.00 : 8048484: 55 push %ebp0.00 : 8048485: 89 e5 mov %esp,%ebp[...]0.00 : 8048530: eb 0b jmp 804853d: count++;14.22 : 8048532: 8b 44 24 2c mov 0x2c(%esp),%eax0.00 : 8048536: 83 c0 01 add $0x1,%eax14.78 : 8048539: 89 44 24 2c mov %eax,0x2c(%esp): memcpy(&tv_end, &tv_now, sizeof(tv_now));: tv_end.tv_sec += strtol(argv[1], NULL, 10);: while (tv_now.tv_sec< tv_end.tv_sec ||: tv_now.tv_usec< tv_end.tv_usec) {: count = 0;: while (count< 100000000UL)14.78 : 804853d: 8b 44 24 2c mov 0x2c(%esp),%eax56.23 : 8048541: 3d ff e0 f5 05 cmp $0x5f5e0ff,%eax0.00 : 8048546: 76 ea jbe 8048532[...] 使用perf annotate分析内核 Perf工具不知道如何从压缩内核镜像(vmlinuz)中提取符号。正如perf report中的示例,用户必须通过-k传递非压缩内核镜像的路径: perf annotate -k /tmp/vmlinux -d symbol 在一次说明,这只使用带debug符号编译的内核。 使用perf top进行现场分析 perf工具可以以类似于Linux top工具的模式运行,实时打印采样函数。默认的采样事件是cycles,默认的顺序是每个符号的采样数递减,因此perf top显示了花费大部分时间的函数。默认情况下,perf top以processor-wide模式运行,在用户和内核级别监视所有在线的CPU。使用-C选项可以只监视CPU的一个子集。 perf top-------------------------------------------------------------------------------------------------------------------------------------------------------PerfTop: 260 irqs/sec kernel:61.5% exact: 0.0% [1000Hzcycles], (all, 2 CPUs)-------------------------------------------------------------------------------------------------------------------------------------------------------samples pcnt function DSO_______ _____ ______________________________ ___________________________________________________________80.00 23.7% read_hpet [kernel.kallsyms]14.00 4.2% system_call [kernel.kallsyms]14.00 4.2% __ticket_spin_lock [kernel.kallsyms]14.00 4.2% __ticket_spin_unlock [kernel.kallsyms]8.00 2.4% hpet_legacy_next_event [kernel.kallsyms]7.00 2.1% i8042_interrupt [kernel.kallsyms]7.00 2.1% strcmp [kernel.kallsyms]6.00 1.8% _raw_spin_unlock_irqrestore [kernel.kallsyms]6.00 1.8% pthread_mutex_lock /lib/i386-linux-gnu/libpthread-2.13.so6.00 1.8% fget_light [kernel.kallsyms]6.00 1.8% __pthread_mutex_unlock_usercnt /lib/i386-linux-gnu/libpthread-2.13.so5.00 1.5% native_sched_clock [kernel.kallsyms]5.00 1.5% drm_addbufs_sg /lib/modules/2.6.38-8-generic/kernel/drivers/gpu/drm/drm.ko 默认情况下,第一列显示自运行开始以来的总样本数。通过按“Z”键,可以将其更改为打印自上次刷新以来的样本数。当处理器不处于暂停状态(即不空闲)时,cycle事件也会统计CPU周期。因此,这不等于墙面时间。此外,事件还受频率扩展的影响。 也可以深入到单个函数中,查看哪些指令具有最多的样本。要深入到指定函数,请按“s”键并输入函数名。这里我们选择了顶部函数noploop(上面没有显示): ------------------------------------------------------------------------------------------------------------------------------------------PerfTop: 2090 irqs/sec kernel:50.4% exact: 0.0% [1000Hz cycles], (all, 16 CPUs)------------------------------------------------------------------------------------------------------------------------------------------Showing cycles for noploopEvents Pcnt (>=5%)0 0.0% 00000000004003a1:0 0.0% 4003a1: 55 push %rbp0 0.0% 4003a2: 48 89 e5 mov %rsp,%rbp3550 100.0% 4003a5: eb fe jmp 4003a5 使用perf bench进行基准测试 perf bench命令包含多个多线程微内核基准测试,用于在Linux内核和系统调用中执行不同的子系统。这使得黑客可以轻松地测量更改的影响,从而帮助缓解性能衰退。 它还充当一个通用的基准框架,使开发人员能够轻松地创建测试用例、透明进行整合和使用富性能工具子系统。 sched:调度器基准测试 测量多个任务之间的pipe(2)和socketpair(2)操作。允许测量线程与进程上下文切换的性能。 $perf bench sched messaging -g 64# Running 'sched/messaging' benchmark:# 20 sender and receiver processes per group# 64 groups == 2560 processes runTotal time: 1.549 [sec] mem:内存访问基准测试 numa: numa调度和MM基准测试 futex: futex压力基准测试 处理futex内核实现的细粒度方面。它对于内核黑客非常有用。它目前支持唤醒和重新排队/等待操作,并强调私有和共享futexes的哈希方案。下面时nCPU线程运行的一个示例,每个线程处理1024个futex来测量哈希逻辑: $ perf bench futex hash# Running 'futex/hash' benchmark:Run summary [PID 17428]: 4 threads, each operating on 1024 [private] futexes for 10 secs.[thread 0] futexes: 0x2775700 ... 0x27766fc [ 3343462 ops/sec ][thread 1] futexes: 0x2776920 ... 0x277791c [ 3679539 ops/sec ][thread 2] futexes: 0x2777ab0 ... 0x2778aac [ 3589836 ops/sec ][thread 3] futexes: 0x2778c40 ... 0x2779c3c [ 3563827 ops/sec ]Averaged 3544166 operations/sec (+- 2.01%), total secs = 10 故障诊断和建议 本节列出了很多建议来避免使用Perf时常见的陷阱。 打开文件的限制 Perf工具所使用的perf_event内核接口的设计是这样的:它为per-thread或per-cpu的每个事件使用一个文件描述符。 在16-way系统上,当您这样做时: perf stat -e cycles sleep 1 您实际上创建了16个事件,从而消耗了16个文件描述符。 在per-thread模式下,当您在同一16-way系统上对具有100个线程的进程进行采样时: perf record -e cycles my_hundred_thread_process 然后,一旦创建了所有的线程,您将得到100*1(event)*16(cpus)=1600个文件描述符。Perf在每个CPU上创建一个事件实例。只有当线程在该CPU上执行时,事件才能有效地度量。这种方法加强了采样缓冲区的局部性,从而减少了采样开销。在运行结束时,该工具将所有样本合计到一个输出文件中。 如果Perf因“打开的文件太多”错误而中止,有以下几种解决方案: 使用ulimit-n增加每个进程打开的文件数。注意:您必须是root 限制一次运行中测量的事件数 限制正在测量的CPU数量 增加打开文件限制 超级用户可以更改进程打开的文件限制,使用 ulimit shell内置命令: ulimit -a[...]open files (-n) 1024[...]ulimit -n 2048ulimit -a[...]open files (-n) 2048[...] 使用build-id表示二进制文件 perf record命令在perf.data中保存者与测量相关的所有ELF镜像的唯一标识符。在per-thread模式下,这包括被监视进程的所有ELF镜像。在cpu-wide模式下,它包括系统上运行的所有进程。如果使用-Wl,--build-id选项,则链接器将生成这些唯一标识符。因此,它们被称为build-id。当将指令地址与ELF映像关联时,build id是一个非常有用的工具。要提取perf.data文件中使用的所有生成id项,请发出: perf buildid-list -i perf.data06cb68e95cceef1ff4e80a3663ad339d9d6f0e43 [kernel.kallsyms]e445a2c74bc98ac0c355180a8d770cd35deb7674 /lib/modules/2.6.38-8-generic/kernel/drivers/gpu/drm/i915/i915.ko83c362c95642c3013196739902b0360d5cbb13c6 /lib/modules/2.6.38-8-generic/kernel/drivers/net/wireless/iwlwifi/iwlcore.ko1b71b1dd65a7734e7aa960efbde449c430bc4478 /lib/modules/2.6.38-8-generic/kernel/net/mac80211/mac80211.koae4d6ec2977472f40b6871fb641e45efd408fa85 /lib/modules/2.6.38-8-generic/kernel/drivers/gpu/drm/drm.kofafad827c43e34b538aea792cc98ecfd8d387e2f /lib/i386-linux-gnu/ld-2.13.so0776add23cf3b95b4681e4e875ba17d62d30c7ae /lib/i386-linux-gnu/libdbus-1.so.3.5.4f22f8e683907b95384c5799b40daa455e44e4076 /lib/i386-linux-gnu/libc-2.13.so[...] build-id缓存 每次运行结束时,perf record命令都会更新一个build id缓存,其中包含带有样本的ELF镜像的新条目。缓存包含: 带样本的ELF镜像的build-id 带有样本的ELF镜像的副本 给定的build-id是不可变的,它们唯一地标识二进制文件。如果重新编译二进制文件,将生成新的build-id,并在缓存中保存ELF图像的新副本。缓存保存在磁盘上的默认目录$HOME/.debug中。系统管理员可以使用全局配置文件==/etc/perfconfig==为缓存指定备用全局目录: $ cat /etc/perfconfig[buildid]dir = /var/tmp/.debug 在某些情况下,关掉 build-id缓存更新可能时有益的。为此,你需要使用perf record的 -n选项 性能记录 perf record -N dd if=/dev/zero of=/dev/null count=100000 访问控制 No permission to collect system-wide stats. 其他场景 分析睡眠时间 此功能显示程序在何处睡眠或等待某物的时间和时间。 第一步是收集数据。我们需要收集sched_stat和sched_switch事件。Sched_stat事件是不够的,因为它们是在任务的上下文中生成的,这会唤醒目标任务(例如释放锁)。我们需要相同的事件,但带有目标任务的调用链。此调用链可以从之前的sched_switch事件中提取。 第二步是合并sched_start和sched_switch事件。这可以通过“perf-inject-s”来完成。 $ ./perf record -e sched:sched_stat_sleep -e sched:sched_switch -e sched:sched_process_exit -g -o ~/perf.data.raw ~/foo$ ./perf inject -v -s -i ~/perf.data.raw -o ~/perf.data$ ./perf report --stdio --show-total-period -i ~/perf.data# Overhead Period Command Shared Object Symbol# ........ ............ ....... ................. ..............#100.00% 502408738 foo [kernel.kallsyms] [k] __schedule|--- __scheduleschedule| |--79.85%-- schedule_hrtimeout_range_clock| schedule_hrtimeout_range| poll_schedule_timeout| do_select| core_sys_select| sys_select| system_call_fastpath| __select| __libc_start_main| --20.15%-- do_nanosleephrtimer_nanosleepsys_nanosleepsystem_call_fastpath__GI___libc_nanosleep__libc_start_main$cat foo.c...for (i = 0; i< 10; i++) {ts1.tv_sec = 0;ts1.tv_nsec = 10000000;nanosleep(&ts1, NULL);tv1.tv_sec = 0;tv1.tv_usec = 40000;select(0, NULL, NULL, NULL,&tv1);}... 参考文档: Linux kernel profiling with perf 三款Profiler工具 补充,三款Linux平台下主流的热点分析工具,分别是GNU gprof、Valgrind和Google perftools,三款工具的主要特点如下表: 工具 使用命令 是否需要重新编译 Profiling速度 是否支持多线程热点分析 是否支持链接库热点分析 GNU gprof ./test; gprof ./test ./gmon.out 是 慢 否 否 Valgrind Valgrind –tool=callgrind ./test 否 非常慢 是 是 Google perftools LD_PRELOAD=/usr/lib/libprofiler.so CPUPROFILE=./test.prof ./test 否 快 是 是 Cpuload测试程序 #include#include#ifdef WITHGPERFTOOLS #include#endif using namespace std; int foo(vectorv) { int result = 0; for(auto x: v) { result += x; } return result % 1000; } int main() { #ifdef WITHGPERFTOOLS ProfilerStart("profile.log"); #endif vectorv; v.push_back(1); int result = 0; for (int i=0; i<10000; i++) { result = foo(v); v.push_back(result); } #ifdef WITHGPERFTOOLS ProfilerStop(); #endif cout<< result<< "\n"; return 1; } GNU gprof 是GNU G++自带的热点分析工具,使用方法是:1. 使用-pg选项重新编译代码;2. 执行程序./test,生成热点分析结果gmont.out;3.使用gprof查看结果gprof ./test ./gmon.out。因为gprof要求用-pg重新编译代码,需要在Debug模式下进行Profiling,所以速度较慢。另外gprof不支持多线程的热点分析。这个工具另一个大问题是,不支持链接库的热点分析。很多大型项目为了模块化管理会生成很多动态链接库供其他程序调用,如果要分析每个模块的热点,这个工具就不适用了。 测试脚本如下: #!/bin/bash # build the program with profiling support (-gp) g++ -std=c++11 -pg cpuload.cpp -o cpuload # run the program; generates the profiling data file (gmon.out) ./cpuload # print the callgraph gprof cpuload Valgrind 是一系列工具的套装,包括内存分析、热点分析等。它的使用非常简单,安装好之后,直接调用Vallgrind中的callgrind工具即可,命令为Valgrind –tool=callgrind ./test。使用该工具Profiling无需重新编译代码,也支持多线程和链接库的热点分析,但是由于Profiling原理的特殊性,其Profiling速度非常之慢,比直接运行程序慢了将近50倍,所以并不适合稍大型程序的热点分析。 测试脚本如下: #!/bin/bash # build the program (no special flags are needed) g++ -std=c++11 cpuload.cpp -o cpuload # run the program with callgrind; generates a file callgrind.out.12345 that can be viewed with kcachegrind valgrind --tool=callgrind ./cpuload # open profile.callgrind with kcachegrind kcachegrind profile.callgrind 与gprof相反,我们不需要使用任何特殊的编译标志来重建应用程序。我们可以像valgrind一样执行任何可执行文件。当然,执行的程序应该包含调试信息,以获得具有人类可读符号名称的表达性调用图。 下面是一个KCachegrind,其中包含cpuload演示的评测数据: Google perftools 原是Google内部的性能分析工具,后来在Github上开源了,地址是https://github.com/gperftools/gperftools。这套工具提供了CPU探查器、快速线程感知malloc实现、内存泄漏检测器和堆探查器。gperftools 的工作原理为通过定期采样当前正在执行的指令进行性能分析,如果某个函数被采样到的次数越多,则该函数在执行时占用的时间比例越大,很可能就是性能瓶颈。 gperftools 可以在被测软件处于 Release 模式下进行性能分析,所以能最大程度的模拟软件的实际使用情况。这个工具使用起来也非常简单,只需Preload其.so文件并指定生成的Profiling文件路径即可,命令为LD_PRELOAD=/usr/lib/libprofiler.so CPUPROFILE=./test.prof ./test。程序结束之后,使用命令google-pprof –web ./test ./test.prof即可查看热点分析结果。使用该工具Profiling无需重新编译代码,也支持多线程和链接库的热点分析,同时由于其是通过定期采样正在执行的指令进行热点分析,所以Profiling速度非常快,和正常release下执行程序的速度几乎相当。通过试用,发现gperftools的易用性、可视化效果等都是最好的,所以推荐大家使用gperftools。 下面主要介绍一下CPU profiler的使用情况。 使用gperftools创建应用程序选定部分的CPU配置文件需要以下步骤: 编译程序时启用调试符号(以获得有意义的调用图),并链接gperftools profiler.so #include,并要使用ProfilerStart(“nameOfProfile.log”);和ProfilerStop()包围需要分析的代码块; 执行程序以生成分析数据文件 要分析评测数据,请使用pprof(与gperftools一起分发)或将其转换为与callgrind兼容的格式,然后使用KCachegrind进行分析。 测试脚本如下: #!/bin/bash # build the program; For our demo program, we specify -DWITHGPERFTOOLS to enable the gperftools specific #ifdefs g++ -std=c++11 -DWITHGPERFTOOLS -lprofiler -g ../cpuload.cpp -o cpuload # run the program; generates the profiling data file (profile.log in our example) ./cpuload # convert profile.log to callgrind compatible format pprof --callgrind ./cpuload profile.log >profile.callgrind # open profile.callgrind with kcachegrind kcachegrind profile.callgrind 结论 gprof是经过评估的剖析器中的恐龙——它的起源可以追溯到20世纪80年代。在过去的几十年里,它似乎得到了广泛的应用,是一个很好的解决方案。但它对多线程应用程序的支持有限,无法分析共享库,需要使用兼容的编译器和特殊标志重新编译,这会产生相当大的运行时开销,因此不适合在当今的实际项目中使用它。 Valgrind提供最精确的结果,非常适合多线程应用。它非常容易使用,并且有KCachegrind用于可视化/分析分析分析分析数据,但是被测应用程序的缓慢执行使其无法用于更大、运行时间更长的应用程序。 gperftools CPU profiler的运行时开销很小,提供了一些很好的功能,比如有选择地分析某些感兴趣的领域,并且对多线程应用程序没有问题。KCachegrind可用于分析分析分析数据。与所有基于采样的探查器一样,它存在统计不准确性,因此结果不如Valgrind准确,但实际上这通常不是什么大问题(如果需要更准确的结果,可以随时增加采样频率)。推荐使用它。 实际项目服务器性能提升经验 网游服务器常会遇到的问题 DB IO的处理 当我们用机器人做压力测试, 往往会发现DB队列总是一个痛点. 绝大部分操作都是道具上的 道具占最多这个是能想到的. 原因可能: 生成一个道具需要写多次DB, 如,一次记录道具本身, 一次记录用来做道具最大ID(算唯一ID用的) 更新一个道具的时候, 很有可能更新了多次 玩家登陆的时候, 会把刚刚load的每个道具都保存一次 这是道具本身实现不太合理的地方, 还有就是机器人程序, 测试程序本身也要设计的比较合理, 不合理的道具压测实现也会造成DB压力非常大. 例如: 某个功能机器人会把所有的装备都删一遍, 然后再加一遍 某个功能机器人可能会不停的添加道具(或者装备), 最后背包满了, 就要往邮件里面塞 类似的功能有很多等等 测试程序本身, 需要比较合理的设计, 尽可能去贴合玩家的真实操作. 玩家的定时存档 大部分操作都是立即存档的, 但是涉及到Player表, 就会延迟存档(大概1-2分钟), 这是MMOG常用的操作. 可能的解决办法,可以把多数玩家集中保存操作打散(每个玩家有自己的保存时间,为了防止玩家集中时间登入游戏,造成savedb相对集中,可以考虑在一段时间内随机), 帧率才稳定. dbIO造成非常多的垃圾对象。可以考虑使用内存池技术,避免生成大量小对象;还有就是减少dbio的操作,可以尝试合批后,一并提交。 DB操作的时间越来越长 db操作的表,或对象是否有建立索引; 减少很多不必要的DB操作,合批; 使用连接池+线程池的办法增加db并发能力; 合理设计数据结构,避免巨大的单条记录,考虑不同属性分表;超大数据库分库等等。 采用商业化的云db产品(如mysql,mongodb,redis等) 广播和网络IO处理 整个核心思想就是减少每一个包上的编解码消耗(以及产生的垃圾对象),并且在一定程度上实现合批(比如,每个逻辑帧合并一批的网络消息;或者消息累加到一定数量/大小,再合并发出). 合理规划同步消息, 例如: 玩家移动消息 因为很多客户端为了提升游戏体验,对移动有预判,不会等到服务器返回便自己开始走,服务返回之后会不断矫正的位置,差别不大就不需要矫正;差异比较大时会有矫正过程,一条客户端消息会可能会有多条服务器消息返回。 一个跳跃有4个左右的消息, 一个滑步有3个左右的消息 有的游戏设定,每次跳跃和滑步都需要使用怒气(能量类似的东西),然后这些东西加减, 也需要同步给所有客户端, 实际上这些可以让客户端自己去模拟和维护. 还有跳跃和滑步也是, 最多1~2个输入就可以完成. 战斗部分 由于很多技能是无目标战斗(AOE技能), 砍一刀很有可能砍刀10个怪, 但是伤害如果发10个怪, 那么就需要做10个编解码, 发10次广播消息。 其他部分 AOI算法,多人同屏的时候,非常重要,常用算法:九宫格、灯塔、十字链表、三轴十字链表等等。 九宫格AOI算法,核心是把整个地图划分成大小相等的正方形格子,每个格子有容器存储格子里的玩家; 灯塔AOI,是把整个地图划分成比较大的格子,每个格子称为一个灯塔,玩家视野一般涉及上下左右4个灯塔,网易的pomelo有实现这个算法; 十字链表,把场景中的实体按位置从小到大用双向链表保存起来,X轴用一条双向链表,Y轴用一条双向链表,因为在画坐标时X轴和Y轴刚好呈十字,所以称十字链表(链表中保存的是一个点;或者,链表中保存的是一条线段)。 内存分配的优化 内存分配情况,需要借助于Visual Studio之类的工具来分析了, 具体可以看前面的文章。 处理方式也比较简单--使用pool,重写内存分配器;找到一个非法使用情况,就fix一个. 比较容易遗漏的小型对象的申请:比如闭包,如闭包在Player处理某个东西时候需要,那么就把闭包和闭包的状态存在Player对象上(闭包也是一个小型对象);临时的容器,这个可以使用threadlocal来优化,每次用的时候clear一下。 还有一个比较关键的是, Linux下native部分的内存分配. 可能服务器在Windows Server下长时间跑, 都没有内存泄漏, 但是在Linux下跑会有内存泄漏, 最后查找原因是非托管部分泄漏了. 可尝试换成jemalloc之后解决。 性能优化的建议 用profile工具优化性能时,内存申请释放可能会影响部分性能(特别是很多小型对象),比如某些语言的GC(如java,c#),会很耗时;频繁的申请释放内存,即使对c或c++这样的语言来说,也是极力避免的。考虑使用内存池技术,提升申请和释放效率。 当内存/GC问题解决掉了之后,之前提到的很多性能问题都可能会被缓解,包括DB执行更新操作耗时越来越长这样的情况(当然首先得是db本身压力不大)。 物理引擎的耗时,很可能会占用整个逻辑帧30%左右的时间片。比如,用一些高效的碰撞器替代计算复杂的碰撞器,比如球型碰撞器的性能 >Box碰撞器 >Mesh碰撞器。对非必要的高频操作降频处理(如调整物理引擎fixedUpdate)等。另外,物理引擎建议和逻辑功能分开成不同的服务器。 每逻辑帧处理的功能越少,计算量越少,在精度等因素允许的情况下尽可能减少每帧的运算量。可以分帧处理某些复杂逻辑。 多线程/并行化,可以使用线程池或并行库(ppl/tbb)等来提升并发处理能力。

用户评论

不忘初心

这个太棒了!我一直想找一个能分析网游服务器性能问题的工具,现在终于找到了!希望能有更多实用性的优化建议教程!

    有18位网友表示赞同!

放肆丶小侽人

游戏卡顿的烦恼真是让人头疼啊,这篇文章给我带来了新的思路。之前一直怀疑是网络问题,没想到还有可能是服务器的问题。要仔细看看这些工具的功能,看看哪一个适合我现在的游戏环境。

    有9位网友表示赞同!

抚涟i

作为一款网游玩家,对 servidores 的稳定性要求非常高。这款收集整理的性能优化工具简直是我的福音!期待在以后还能看到更多详细的测试分析和教程,这样才能更好地提升游戏体验。

    有8位网友表示赞同!

顶个蘑菇闯天下i

作为一个程序员,我对服务器管理一直感兴趣,这篇文章让我眼前一亮!这些工具不仅可以提高网游运行效率,还可以为后端开发人员提供很多宝贵的性能数据。希望能有相关书籍或视频课程做更深入的讲解。

    有17位网友表示赞同!

小清晰的声音

这些工具听起来都很专业,但我作为一个不了解服务器参数的用户,感觉有点难理解。还是希望能够提供一些更加直观的介绍和使用教程,这样小白也能上手!

    有15位网友表示赞同!

青衫故人

网游服务器性能优化一直是一个需要重视的问题,这篇文章做得很全面,汇总了各种类型的工具,可以根据不同的需求进行选择。但部分工具的描述不够详细,希望能添加更多实用的技术细节。

    有15位网友表示赞同!

短发

网游服务器稳定性非常重要,尤其是热门游戏服务器。希望这些工具能够帮助到开发团队,提高玩家游戏体验!

    有13位网友表示赞同!

青山暮雪

感觉这些工具的功能都比较强大,但是上手难度可能比较高。建议可以提供一些使用案例和实战经验分享,方便用户更好地学习和应用。

    有13位网友表示赞同!

把孤独喂饱

作为一款网游运营者,对于服务器性能优化非常重视。这篇文章虽然有收集整理了许多工具,但我还是希望能看到更多针对特定类型的网游的优化方案和具体案例,这样更加实用!

    有14位网友表示赞同!

江山策

服务器性能优化是一件很复杂的事情,需要考虑很多因素。希望这些工具能够帮助开发者更直观地分析问题,找到解决方案。

    有18位网友表示赞同!

罪歌

这篇文章真的很有用!我一直在寻找一些专业的网游服务器性能优化工具,没想到找到了这个整理的好资源!希望能有更多类似的文章分享不同类型的工具和案例研究,这样可以更加深入的了解服务器性能优化相关知识!

    有11位网友表示赞同!

眼角有泪°

作为一个游戏爱好者,我最关心的就是游戏的流畅性。希望这些工具能够帮助开发团队解决服务器问题,让我们玩家能够体验到更完美的游戏世界!

    有6位网友表示赞同!

▼遗忘那段似水年华

网游服务器优化确实是个技术活,需要专业人员才能操作。对于普通玩家来说,可以关注相关的博文和教程,学习一些基本的知识。毕竟,一款流畅的网游才是玩家们的最爱!

    有14位网友表示赞同!

醉红颜

这些工具看起来很有潜力,希望能帮助开发团队提升游戏的稳定性和用户体验!期待看到更多关于性能优化技术的文章分享!

    有6位网友表示赞同!

雨后彩虹

虽然这篇收集整理挺详细,但是对于一些不太熟悉的技术词汇还是难以理解。希望以后的文章能够加入更详细的讲解,方便新手入门!

    有11位网友表示赞同!

热点资讯