分析单元会使分配消失

尼克·卡平斯基

在VisualStudio17.8Preview 2中,我们更新了单元测试分析,允许您使用性能分析器中的任何可用工具,而不仅仅是检测工具。有了这个更改,很容易单独快速分析小的工作单元,进行更改,然后重新测量和验证更改的影响。假设您有良好的测试覆盖率,这是一种利用现有资产帮助调整应用程序性能的好方法。

谁动了我的奶酪?

通过这个新版本,我们更新了单元测试分析经验。以前,当在单元测试的上下文菜单中选择概要文件时,它在Instrumentation工具下运行,并且在运行结束时得到报告。测试资源管理器在上下文菜单中显示配置文件命令

现在,当您选择一个配置文件时,将显示Performance Profiler启动页面,您可以选择任何可用的工具。Visual Studio探查器启动页

这使您可以使用执行类似于分析单元测试的操作。NET对象分配工具查看所有分配及其来源。这是一种减少不必要的分配,然后验证更改的好方法。

让我们减少一些分配!

现在我们可以使用任何工具了,让我们花5分钟看看是否可以从性能分析器中调整一些分配。首先,我有一个名为“VerifySimpleCallTree”的单元测试,我们使用它来验证我们的探查器是否正确地构建了一个调用树。在测试浏览器中,我右键单击测试并选择“Profile”,显示Performance Profiler,选择。NET对象分配工具,然后单击“开始”。从这里,我的测试运行并完成后,我得到了正常的分配报告,我可以使用该报告深入研究分配情况,并查看可以修改哪些内容以减轻GC的负担。

显示枚举器分配的.NET分配工具当我仔细查看这些类型时,我注意到分配了一堆枚举器。虽然这并不是马上就错了,但看起来确实很奇怪。通过回溯,我可以看到这是来自我们的JmcConfigurationService,并且通过查看代码,可以确定正在从Any()扩展方法创建枚举器。

this.patternsLock(此模式锁定)。进入ReadLock();尝试{if(this.unknownModulePatterns.Any(t=>t.IsMatch(moduleString)){return JmcState。未知代码;}else if(this.systemModulePatterns.Any(t=>t.IsMatch(moduleString))){返回JmcState。系统代码;}其他的{返回IsStringJmc(moduleString,this.excModulePatterns,this.incModulePatterns)?Jmc州。用户代码:JmcState。图书馆代码;}}最后{this.patternsLock(此模式锁定)。ExitReadLock();}

通过使用静态局部函数进行快速重写,我们可以删除枚举器并减少分配。

this.patternsLock(此模式锁定)。进入ReadLock();尝试{if(CheckMatch(this.unknownModulePatterns,moduleString)){返回JmcState。未知代码;}else if(CheckMatch(this.systemModulePatterns,moduleString)){return JmcState。系统代码;}其他的{返回IsStringJmc(moduleString,this.excModulePatterns,this.incModulePatterns)?Jmc州。用户代码:JmcState。图书馆代码;}}最后{this.patternsLock(此模式锁定)。ExitReadLock();}静态bool CheckMatch(列表模式,字符串模块Str){foreach(模式中的var模式){if(pattern.IsMatch(moduleStr)){返回true;}}返回false;}

在单元测试中重新运行分析器,然后我可以验证是的;事实上,我已经删除了这些不必要的分配,并帮助减轻了GC的负担。显示枚举器分配减少的.NET分配工具

虽然这个小小的调整不会让我的应用程序神奇地快20%,但随着时间的推移,慢慢减少不必要的分配是逐步提高应用程序性能的好方法。使用新的单元测试概要分析,可以很容易地对现有的测试资产进行分析,然后验证更改是否产生了所需的影响。

 

让我们知道你的想法!

使用单元测试进行独立性能分析的能力非常棒。通过隔离特定的代码区域,很容易获得良好的前后跟踪,以比较和查看性能优化的影响。我们热忱欢迎任何好主意、想法或有价值的见解,我们洗耳恭听!不要犹豫,在这里与我们分享链接.

11条评论

讨论结束。登录以编辑/删除现有评论。

  • 奥努尔·古穆什 0

    另一种选择是使用List。通过表示委托的静态内部函数查找🙂

    • 尼克·卡平斯基Microsoft员工 1

      是的,这是另一个有效的解决方案。它具有挑战性,因为不同的实现非常相似,但具有不同的分配/CPU性能特征。在工具显示之前,我从未想过我们会分配这么多枚举器🙂

  • 加博尔·萨博 0

    您应该立即将foreach转换为for循环。你应该抄下来

    *图案

    变量预先放入新数组(如果不使用数组)。

    • 尼克·卡平斯基Microsoft员工 1

      根据这个夏普列表Gist传统的for循环与foreach JIT相同。但绝对值得检查🙂

  • 阿什什·辛格 0

    是否可以在CI管道中使用此功能以观察一段时间内的分配行为?观察历史上绩效改进的进展情况。

    • 匿名的 0

      此评论已被删除。

    • 尼克·卡平斯基Microsoft员工 1

      目前还没有,尽管这是我们正在努力解决的问题。面临的挑战是,由于事情可以分层JIT或在运行时更改,分配并不总是一致的。我们正在研究如何将这样一个无噪音、有助于保护所有性能提升的系统组合在一起。

  • 帕西·萨沃莱恩 5

    不提供“专业”风格,超过95%。

    • 杰里米·鲍威尔

      我同意,似乎有理由期望这种功能应该是专业版

    • 科伦蒂·阿尔特佩 0

      我很困惑,因为我甚至在更新后也找不到VS2022中的功能。我不明白,这个功能只在企业版中提供吗?还是只在社区中,而不在专业版/企业版中?为什么?

  • 丹尼尔·马尔巴赫 0

    你好,Nik,

    谢谢你的这篇优秀文章。我今年在NDC Oslo做了一个关于闭包、LINQ到基于集合的优化以及更多你可能会觉得有趣的话题的演讲。

    https://www.youtube.com/watch?v=pGgsFW7kDKI&t=150s

    当做,
    丹尼尔

反馈usabilla图标