mcpField
B
build-perf-diagnostics

build-perf-diagnostics

github.com/dotnet开发5.8k3.6k 次安装1 天前更新
代码仓库

关于 build-perf-diagnostics

这项技能通过二进制日志分析并改善 MSBuild 性能,面向处理构建缓慢问题的开发者。它结合 binlog MCP 查询与日志回放诊断,并提供常见瓶颈的分析指引。

主要功能

  1. 01生成并检查构建 binlog
  2. 02分析目标和任务耗时
  3. 03检查节点利用率与分析器
  4. 04识别常见构建瓶颈
  5. 05按影响程度排列发现

适用场景

  1. 01找出 .NET 构建中的慢速目标
  2. 02调查并行构建利用率偏低
  3. 03评估分析器或复制任务的开销
  4. 04安排构建性能改进的优先级

MCP 相关

使用 `binlog` 命名空间中的捆绑 `Microsoft.AITools.BinlogMcp` 服务进行性能分析。MCP 不可用时,文档提供将 binlog 回放为诊断文本日志的方法。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dotnet/skills --skill build-perf-diagnostics

安装后在 Claude Code 中输入/build-perf-diagnostics确认已加载。

详细介绍

性能分析方法

  1. 生成 binlog:dotnet build /bl:{} -m
  2. 使用此插件捆绑的 binlog MCP 服务(Microsoft.AITools.BinlogMcp,通过 binlog MCP 命名空间公开)

如果服务无法连接,并且需要修复启动问题,请使用 troubleshoot-binlog-mcp。如果修复受阻、被拒绝或没有必要,请使用下面的回放流程。

MCP 不可用时的替代流程:将 binlog 回放为文本日志
  1. 生成 binlog:dotnet build /bl:{} -m
  2. 回放为包含性能摘要的诊断日志:
    dotnet msbuild build.binlog -noconlog -fl "-flp:v=diag;logfile=full.log;performancesummary"
    
  3. 查看性能摘要(位于 full.log 末尾):
    grep "Target Performance Summary\|Task Performance Summary" -A 50 full.log
    
  4. 找出耗时较长的目标和任务:PerformanceSummary 部分会按累计耗时排序,列出所有目标和任务
  5. 检查节点利用率:搜索调度和节点消息
    grep -i "node.*assigned\|building with\|scheduler" full.log | head -30
    
  6. 检查分析器:搜索分析器计时信息
    grep -i "analyzer.*elapsed\|Total analyzer execution time\|CompilerAnalyzerDriver" full.log
    

关键指标和阈值

  • 构建时长:什么算“正常”——小型项目 <10s,中型项目 <60s,大型项目 <5min
  • 节点利用率:理想情况下,各节点的活动时间占比应 >80%。利用率低 = 序列化瓶颈
  • 单个目标占主导:如果某个目标占构建时间的 >50%,请进行调查
  • 分析器耗时与编译耗时:分析器耗时应 <Csc 任务耗时的 30%。如果更高,请考虑移除耗时较长的分析器
  • RAR 耗时:ResolveAssemblyReference 耗时 >5s 值得关注。>15s 则属于异常

常见瓶颈

1. ResolveAssemblyReference(RAR)缓慢
  • 症状:每个项目的 RAR 耗时 >5s
  • 根本原因:程序集引用过多、引用路径基于网络、程序集搜索路径过长
  • 修复方法:减少引用数量;对 RAR 开销大的分析使用 <DesignTimeBuild>false</DesignTimeBuild>;对诊断设置 <ResolveAssemblyReferencesSilent>true</ResolveAssemblyReferencesSilent>
  • 进阶:<DesignTimeBuild> 和 <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  • 关键见解:即使在增量构建中,RAR 也会无条件运行,因为用户可能安装了目标框架包或将程序集添加到了 GAC(参见 dotnet/msbuild#2015)。使用 .NET Core 微型程序集时,引用数量通常非常多。
  • 减少传递引用:设置 <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>,避免引入完整的传递闭包(注意:项目可能需要为所用的类型添加直接引用)。对于仅在构建时需要(不属于 API 表面)的 ProjectReferences,使用 ReferenceOutputAssembly="false"。移除未使用的 PackageReferences。
2. Roslyn 分析器和源生成器
  • 症状:相对于文件数量,Csc 任务耗时远超预期(>干净编译时间的 2 倍)
  • 诊断:查看回放日志中的 Task Performance Summary,了解 Csc 任务耗时;搜索分析器计时信息;比较启用和禁用分析器时的 Csc 耗时(/p:RunAnalyzers=false)
  • 修复方法:
    • 有条件地在开发过程中禁用:<RunAnalyzers Condition="'$(ContinuousIntegrationBuild)' != 'true'">false</RunAnalyzers>
    • 按配置设置:<RunAnalyzers Condition="'$(Configuration)' == 'Debug'">false</RunAnalyzers>
    • 仅启用代码风格分析:<EnforceCodeStyleInBuild Condition="'$(ContinuousIntegrationBuild)' == 'true'">true</EnforceCodeStyleInBuild>
    • 从日常开发循环中移除确实多余的分析器
    • 在 .editorconfig 中设置严重性,以降低不太重要规则的级别
  • 核心原则:在 CI 中保留分析器检查。切勿直接“移除”分析器,而应通过条件进行配置。
  • GlobalPackageReference:通过 Directory.Packages.props 中的 GlobalPackageReference 添加的分析器会应用于所有项目。请考虑测试项目是否需要与生产代码相同的分析器集。
  • EnforceCodeStyleInBuild:在 Directory.Build.props 中设为 true 时,会在每次构建时强制执行代码风格分析。应根据 CI 环境(ContinuousIntegrationBuild)进行条件设置,以避免拖慢开发迭代。
3. 串行化瓶颈(单线程目标)
  • 症状:性能摘要显示,大部分构建时间集中在单个项目;诊断日志显示某个节点正在工作,而其他节点处于空闲状态
  • 常见原因:目标未正确声明依赖项,单个项目位于关键路径上
  • 解决方法:拆分大型项目,优化关键路径上的项目,确保正确设置 BuildInParallel
4. 过多文件 I/O(复制任务)
  • 症状:复制任务的累计耗时较高
  • 根本原因:复制数千个文件、跨网络驱动器复制、复制任务意外地按每个项目运行一次(逐文件),而非作为单个批次运行(见 dotnet/msbuild#12884)
  • 解决方法:使用硬链接(<CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>true</CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>),减少 CopyToOutputDirectory 项,适当时使用 <UseCommonOutputDirectory>true</UseCommonOutputDirectory>,设置 <SkipCopyUnchangedFiles>true</SkipCopyUnchangedFiles>,考虑使用 --artifacts-path(.NET 8+)集中管理输出
  • Dev Drive:在 Windows 上,切换到 Dev Drive(使用写时复制、减少 Defender 扫描的 ReFS)可显著降低复制密集型构建的文件 I/O 开销。建议开发计算机和可自托管的 CI Agent 都使用 Dev Drive。
5. 求值开销
  • 症状:在任何编译开始前,构建启动缓慢
  • 根本原因:Directory.Build.props 过于复杂、通配符 glob 扫描大型目录、NuGetSdkResolver 开销(即使已还原,每次项目求值也会增加 180-400ms,见 dotnet/msbuild#4025)
  • 解决方法:降低 Directory.Build.props 的复杂度;对于使用显式文件列表的旧项目,使用 <EnableDefaultItems>false</EnableDefaultItems>;尽可能避免使用基于 NuGet 的 SDK 解析器
  • 另见:eval-performance 技能,了解详细指南
6. 构建期间还原 NuGet
  • 症状:即使没有必要,每次构建都会运行还原
  • 解决方法:
    • 将还原与构建分开:先运行 dotnet restore,再运行 dotnet build --no-restore
    • 启用静态图求值:在 Directory.Build.props 中设置 <RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation> — 在大型构建中可显著节省时间(效果取决于工作负载)
  • 另见:当多个管道或脚本步骤各自执行还原时,或在添加 --no-restore 之前,使用 eliminate-redundant-restores 技能(如果没有更早的还原步骤覆盖当前步骤,它会因 NETSDK1004/NETSDK1047 而失败)
7. 项目数量过多和依赖图形状
  • 症状:有许多小型项目,每个项目耗时都很短,但开销累积起来;深层依赖链会导致构建串行化
  • 可考虑:合并项目,或使用 /graph 模式改进调度
  • 依赖图形状很重要:宽依赖图(层级少、并行分支多)比深依赖图(层级多、串行化)构建更快。将深层结构重构为宽层结构,可显著缩短全新构建和增量构建时间。
  • 操作建议:检查是否存在不必要的项目依赖,考虑将瓶颈项目拆分为两个项目,或合并小型叶子项目

使用 Binlog 重放进行性能分析

使用文本日志重放的分步工作流:

  1. 重放并生成性能摘要:
    dotnet msbuild build.binlog -noconlog -fl "-flp:v=diag;logfile=full.log;performancesummary"
    
  2. 查看目标/任务性能摘要(位于 full.log 末尾):
    grep "Target Performance Summary\|Task Performance Summary" -A 50 full.log
    
    这会按累计耗时排序,显示所有目标和任务 — 相当于找出耗时较长的目标/任务。
  3. 查找各项目的构建耗时:
    grep "done building project\|Project Performance Summary" full.log
    
  4. 检查并行度(多节点调度):
    grep -i "node.*assigned\|RequiresLeadingNewline\|Building with" full.log | head -30
    
  5. 检查分析器开销:
    grep -i "Total analyzer execution time\|analyzer.*elapsed\|CompilerAnalyzerDriver" full.log
    
  6. 深入分析特定的慢速目标:
    grep 'Target "CoreCompile"\|Target "ResolveAssemblyReferences"' full.log
    

快速见效清单

  • 使用 /maxcpucount(或 -m)进行并行构建
  • 将还原与构建分开(先运行 dotnet restore,再运行 dotnet build --no-restore)
  • 启用静态图还原(<RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation>)
  • 为 Copy 启用硬链接(<CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>true</CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>)
  • 在开发内循环中按条件禁用分析器:<RunAnalyzers Condition="'$(ContinuousIntegrationBuild)' != 'true'">false</RunAnalyzers>
  • 启用引用程序集(<ProduceReferenceAssembly>true</ProduceReferenceAssembly>)
  • 检查增量构建是否存在问题(参见 incremental-build 技能)
  • 检查 bin/obj 是否冲突(参见 check-bin-obj-clash 技能)
  • 对多项目解决方案使用图构建(/graph)
  • 使用 --artifacts-path(.NET 8+)集中管理输出目录结构
  • 在 Windows 开发计算机和可自托管的 CI 环境中启用 Dev Drive(ReFS)

影响程度分类

报告发现的问题时,请按影响程度分类,以便确定修复优先级:

  • 🔴 高影响(优先处理):耗时超过总构建时间 10% 的项目,或单个目标耗时超过构建时间 50%
  • 🟡 中等影响:耗时占构建时间 2%~10% 的项目
  • 🟢 快速见效:改动简单且影响适中(例如在 Directory.Build.props 中设置属性标志)

评论

支持 Markdown