为了账号安全,请及时绑定邮箱和手机立即绑定

编写高性能.net代码--性能评估及工具

标签:
C#

昨天脑图有背景色,影响阅读,今天调整了下

1 性能评估及工具

●1.1 选择评估内容

●举个例子:内存

在做性能优化计划或研究时,应该自上而下地进行。先确保程序结构和算法的合理性,再往下面几层推进。宏观的优化(macro-optimization)总是比微观优化(micro-optimization)更加有效

●好的指标–可测量的数字

差的目标:用户界面应该响应迅速。好的目标:任何操作都不会阻塞UI线程超过20 ms。但只是能被量化还不够,还需要十分精确,正如前面的内存优化案例中所述。差的目标:内存占用应该小于1 GB。好的目标:当负载为每秒100个请求时,工作集内存的占用不能超过1 GB。

●目标值的决定因素–应用程序的类型

带有用户界面的程序必须不惜一切代价保证UI线程的响应能力,无论执行任何任务时都应如此。而服务器端程序每秒要处理几十、几百,甚至几千个请求。它必须非常高效地完成I/O操作和数据同步,以保证吞吐量和CPU利用率的最大化。因此服务器端程序的设计完全不同于其他程序。如果某个应用程序的基础架构先天不足,对效率问题考虑欠佳,那么再回头去修正就很难了

●设计阶段的重要性

●理解整体架构和约束条件

在设计阶段时,你需要理解整体架构和约束条件,不然你就会遗漏一些关键点,这将严重制约程序的运行。你必须在设计阶段就把性能目标预先考虑进去

●安全性

在软件设计阶段,就得考虑安全性等很多方面的问题。性能问题也一样,不能事后再议,必须从一开始就提出明确的目标。要想从头开始把一个已有的应用程序重新设计一遍,这是不可能的,这比一开始就考虑周全要付出多得多的代价

●性能分析

在项目初始阶段的性能分析,与开发完成即将进入测试阶段的分析是不一样的。在初始阶段必须得保证设计的灵活性,确保技术路线在理论上能完成任务,确保在架构上没有大的问题以免除后患。一旦项目进入测试、部署和维护阶段,就得把更多的精力投入微观优化、具体代码方式的分析、减少内存占用等工作

●优化的定律

最后,你还需要了解阿姆达尔定律(Ahmdals’s Law,参见http://www.writinghighperf.net/go/3[PDF]),特别是其应用于顺序执行程序的情况,以便能找到哪部分程序是需要优化的。那些不能明显改善整体性能的微观优化,多半是在浪费时间。为了获得最佳效果,应该优先优化那些效率最低的部分。优化永远不可能面面俱到,得有一个明智的起点。因此,准备好优化目标,再有一套优秀的评估系统,这些都是十分重要的。不然你连从哪儿开始都不知道。

●1.2 平均值还是百分位值

这里有18个样本数据,平均值为17 ms,但95%百分位值远大于50ms。如果只看平均值,你也许不会注意到垃圾回收引起的延时问题,但有了百分位值,判断就更加全面,你会发现垃圾回收过程有时候的性能会很差

●如果只看平均值,你也许不会注意到垃圾回收引起的延时问题,但有了百分位值,判断就更加全面

●中间值(50%百分位值)与平均值的差距相当大。那些占比高的数值,对平均值的影响往往较大

●对于可用性要求很高的服务,百分位值通常要重要得多。可用性要求越高,需要跟踪评估的百分位值就越高

●1.3 评估工具

●评估、评估、再评估

你确实可以只靠查看代码或者直觉获得很多经验,也会获得一些哪里存在性能问题的强烈暗示。你甚至会找对地方,但请彻底打消省略性能评估的念头,除非是些微不足道的问题。原因有两个

●精确找到性能问题所在

●不清楚出错的频率

●如果缺少有效的评估工具,性能的优化就毫无意义

●Visual Studio

●Visual Studio Standalone Profiler

●在Concurrency模式下,如果使用锁或者其他同步对象时发生了资源访问冲突,将会把所有事件记录在案。假如因为资源争用导致了线程阻塞,利用这个模式就会知晓。关于异步编程及统计锁争用次数的详细信息

●ETW即Event Tracing for Windows,是由操作系统提供的事件日志,速度很快,效率也很高。所有应用程序都会生成事件,探查器(Profiler)可以捕获这些事件进行各种分析。第8章介绍了如何在应用程序中充分利用ETW事件,包括捕获预置事件及定义自己的事件

●1.3.2 性能计数器-- PerfMon.exe

●物理内存

●虚拟内存

●保留内存

●已提交内存

物理存在的一段内存,既可能位于RAM中,也可能是在磁盘上

●内存页

内存单位。每页包含了多个已分配的内存块,内存块的单位通常是KB

●页面交换

在多个虚拟内存区域之间交换内存页的过程。内存页既可能与其他进程交换(软交换,Soft Paging),也可能与硬盘交换(硬交换,Hard Paging)。软交换的速度可以非常快

●调入内存页

把内存页从其他地方送入当前进程。

●调出内存页

把内存页从当前进程送出至其他地方,比如磁盘

●上下文切换

保存和恢复线程或进程状态的过程。因为线程数目通常总是多于可用处理器数,所以往往每秒会发生多次上下文切换

●内核模式

该模式下允许操作系统修改底层硬件参数,比如修改某些寄存器,或是启用/禁用中断。切换到内核模式需要调用操作系统API,并且开销相当大

●用户模式

用于执行普通指令的非特权模式,此时无法修改系统底层参数

●Privileged Time

执行特权指令(内核模式)的时间开销。

●Processor Time

应用程序占用单个处理器的百分比。如果应用程序占用了2个逻辑处理器,每个都是100%,那么本计数器值将会是200

●User Time

执行非特权指令(用户模式)的时间开销

●IO Data Bytes/sec——I/O数据量

●Page Faults/sec——缺页中断总数

每当有一页内存不在当前内存工作集中时,就会触发缺页中断。重点是本数值既包含内存软缺页(Soft Page Fault),又包含内存硬缺页(Hard Page Fault)。软缺页对性能没什么大碍,可能是为了调取已加载但不属于当前进程的内存页(比如属于共享DLL的内存页)。硬缺页对性能的影响则要严重得多,因为这意味着数据位于磁盘而非内存中。不幸的是,你无法通过性能计数器跟踪每个进程的硬缺页情况,但可以利用Memory\Page Reads/sec计数器观察到整个系统的硬缺页情况。你可以将某个进程的缺页中断总数和系统的内存读取总页数(硬缺页)结合起来,进行关联分析。通过ETW跟踪Windows Kernel/Memory/Hard Fault事件,可以准确地跟踪某个进程的硬缺页情况。

●Pool Nonpaged Bytes

通常这部分内存是分配给操作系统和驱动程序的,用于存放不允许被页面交换出去的数据,比如线程和互斥锁(Mutex)之类的操作系统对象,以及自定义数据

●Pool Paged Bytes——同样用于存放操作系统数据,但这些数据允许被页面交换出去

●Private Bytes——某个进程专有的已提交虚拟内存(未与其他进程共享)

●Virtual Bytes——进程地址空间内已分配的内存,有些可能是由页面交换文件提供的,有些是与其他进程共享的,还有些内存则是进程专有的

●Working Set——当前驻留在物理内存(通常是RAM)中的虚拟内存数量

●Working Set-Private——当前驻留在物理内存中的进程专有内存数量(Private Bytes)

●Thread Count——进程中的线程数,且与.NET线程数无关。关于.NET线程相关的计数器

●Objects——内核对象(kernel-owned object)数据,比如事件、互斥锁、进程、线程、信号量、临界区

●System——上下文交换、内存对齐修正、文件操作、进程、线程等数量

●ETW系统的效率非常高,可以用最小的开销处理大量事件

每个事件还带有一个由Provider定义的自定义数据字段,描述了某些状态信息。比如Runtime的垃圾回收事件会给出当前属于第几代垃圾回收、是否后台回收等信息

●内存/硬件错误(Memory/Hard Fault)。

●磁盘读取(DiskIO/Read)。

●磁盘写入(DiskIO/Write)。

●进程启动(Process/Start)。

●进程停止(Process/Stop)。

●TCP/IP建立连接(TcpIp/Connect)

●TCP/IP断开连接(TcpIp/Disconnect)。

●线程启动(Thread/Start)。

●线程停止(Thread/Stop)

●1.3.4 PerfView

●以下是一些我经常被问到的PerfView使用问题

●CPU占用率在哪里显示?●什么程序分配到的内存最多?●已被分配最多的资源是什么?●什么原因导致了第2代垃圾回收?●第0代垃圾回收平均多久发生一次?●我的代码JIT编译花了多长时间?●竞争最激烈的是什么锁?●我的内存托管堆情况如何?

●用PerfView进行事件收集和分析的基本步骤

1.在“Collect”菜单中选择“Collect”菜单项。2.在弹出的对话框中设置所需参数。a.展开“Advanced Options”选择需要捕获的事件类型,尽量缩小范围 b.如果当前版本不是.NET 3.5,请选中“No V3.X NGEN Symbols”勾选框。c.可以指定“Max Collect Sec”参数,以便在该指定时间后自动停止收集工作。3.单击“Start Collection”按钮。4.如果未设置“Max Collect Sec”参数,请在数据收集完成后单击“Stop Collection”按钮。5.等待事件分析完成。6.在结果树中选择各种视图来查看结果

●PerfView貌似大多是在对内存和CPU进行分析,但请别忘记它真的只是一个通用的调用栈收集程序,这些调用栈可能来自任何ETW事件。PerfView可以用来分析锁竞争的来源、磁盘I/O等所有应用程序事件,同样提供了强大的分组和折叠显示能力。

●1.3.5 CLR Profiler

●1.3.6 Windbg 很强大

●Windbg的查看功能

●内存堆中每一类对象的数量有多少

●每个内存堆有多大,哪些是空闲的(碎片情况)

●某次垃圾回收之后还有哪些对象驻留着

●哪些对象是被固定的(Pinned)

●哪些线程的CPU耗时最多,是否有线程陷入了死循环

●1.3.7 .NET IL分析器

●反编译软件Reflector、ILSpy、dotPeek

●1.3.8 MeasureIt

●微型性能基准测试工具; 分别显示各种.NetAPI的相对开销,包括调用,数组、委托Delegates、迭代Iteration、反射Reflection

●MeasureIt的主要用途就是,在API级别把软件设计对性能的影响显示出来。比如在lock类中,你会发现使用ReaderWriteLock会比常规的lock语句慢4倍左右

●在MeasureIt的代码中加入自己的测试是非常容易的,它已经自带了源代码,运行MeasureIt /edit就可以解包出来。通过研究这些代码,会让你对编写精确的测试代码产生很大启发

●1.3.9 代码中的工具

●stopwatch 更加准确

●1.3.10 SysInternals工具

●ClockRes 显示系统时钟的精度

●Diskmon 监视硬盘活动

●Handle 监视进程打开了那些文件

●ProcDump 高度可定制化的进程转储文件生成工具

●Process Explorer 非常强大的任务管理器

●Process Monitor 实时监视文件、注册表、进程的活动

●VMMap 分析进程的地址空间

●1.4 小结

●提升性能的首要法则就是评估、评估、再评估

●知道该为你的应用程序使用什么性能指标是非常重要的,每个指标都应该是精确、可量化的

●平均值是很不错,但百分位值也同样要重视,特别是针对高可用性的服务而言

●在前期设计阶段就要把性能目标考虑在内,理解系统架构对性能的影响程度。先从那些影响最大的部分开始着手优化。首先关注算法及整体性的宏观优化,然后再转移到微观优化中去

●应该充分了解性能计数器和ETW事件

●要善用工具软件进行性能分析和调试。请学习如何使用Windbg和PerfView这类最强大的工具,以便快速解决性能问题

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消