关于 eval-performance
面向 MSBuild 项目的评估性能诊断技能,要求先用测量确认瓶颈,再检查导入、Glob、属性函数及重复评估等因素。
主要功能
- 01区分评估与执行耗时
- 02使用 binlog 分析评估时长
- 03检查导入链与全局属性
- 04排查昂贵的 Glob 和属性函数
- 05根据测量结果提出优化
适用场景
- 01调查构建启动阶段耗时
- 02确认项目是否被重复评估
- 03定位导入或文件匹配造成的开销
MCP 相关
指导使用 Microsoft.AITools.BinlogMcp 的 evaluations、imports 和 properties 等 MCP 工具检查评估性能。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dotnet/ skills --skill eval-performance安装后在 Claude Code 中输入/eval-performance确认已加载。
详细介绍
诊断 MSBuild 评估性能
评估是 MSBuild 在任何目标运行之前执行的工作,包括读取项目文件、处理导入、展开 glob。此技能可帮助你发现并确认评估瓶颈。先测量;只有测量结果证明有必要时,才建议更改。
更改任何内容之前,先确认问题
只有在评估被测量证实为瓶颈时才采取行动。遇到以下情况时,不要采取行动:
- 缓慢发生在编译或目标执行期间,而非评估期间。
这不是评估问题——请改用
build-perf-diagnostics。 - **问题是“重建次数太多”/增量构建。**请改用
incremental-build。 - **没有测量数据。**如果没有 binlog 或计时摘要显示评估缓慢,请先收集数据(见下文)。不要仅凭查看项目文件来猜测。
- **出现了下述模式,但评估已经很快。**只有在数据表明会消耗实际时间时,宽泛 glob、深层导入或
EnableDefaultItems才值得指出。评估很快的项目无需更改。
如果存在某种模式但尚未测量,请将其作为观察结果报告,并让用户决定——不要在没有证据表明会显著增加评估耗时的情况下,为了符合“最佳实践”而重写有效配置。优先选择最小且有针对性的更改;不要一开始就禁用 SDK 默认设置。
MSBuild 评估阶段
如需全面了解 MSBuild 的评估和执行模型,请参阅构建过程概述。
- 初始属性:环境变量、全局属性、保留属性
- 导入和属性评估:处理
<Import>,从上到下评估<PropertyGroup> - 项定义评估:
<ItemDefinitionGroup>元数据默认值 - 项评估:包含
Include、Remove、Update和 glob 展开的<ItemGroup> - UsingTask 评估:注册自定义任务
关键点:评估发生在任何目标运行之前。评估缓慢 = 构建启动缓慢,即使没有内容需要编译也是如此。
诊断评估性能
首选:binlog MCP
使用 binlog MCP 服务(Microsoft.AITools.BinlogMcp,通过 binlog MCP 命名空间公开)分析评估性能:
- 使用 evaluations 工具列出所有评估及其耗时
- 使用 evaluation_global_properties 检查是否存在全局属性不同的多次评估
- 使用 evaluation_properties 检查特定项目和 TFM 的已评估属性
- 使用 imports 工具分析导入链的深度和结构
- 使用 properties 工具检查耗时较长的属性函数评估
备用方案:文本日志重放和预处理(MCP 不可用时)
使用 binlog
- 重放 binlog:
dotnet msbuild build.binlog -noconlog -fl "-flp:v=diag;logfile=full.log" - 搜索评估事件:
grep -i 'Evaluation started\|Evaluation finished' full.log - 同一项目出现多次评估 = 过度构建
- 查找“Project evaluation started/finished”消息及其时间戳
使用 /pp(预处理)
dotnet msbuild -pp:full.xml MyProject.csproj- 显示所有导入均已内联的完整展开项目
- 用于了解:导入了哪些内容、导入深度、内容总量
- 预处理输出较大(超过 10K 行)= 评估负担较重
使用 /clp:PerformanceSummary
- 将其添加到构建命令中,以获取耗时明细
- 单独显示评估耗时,以及目标/任务执行耗时
开销较大的 Glob 模式
只有在测量结果表明项评估较慢且原因是 glob 时,才采用这些补救措施;不会遍历大型目录树的自定义 glob 没有问题。
- 类似
**/*.cs的 glob 会遍历整个目录树 - SDK 默认 glob 已经过优化,但自定义 glob 可能没有
- 问题:对
node_modules/、.git/、bin/、obj/执行 glob 操作——这些目录中可能有数百万个文件 - 补救措施:使用
<DefaultItemExcludes>排除大型目录 - 补救措施:明确指定 glob 路径:使用
src/**/*.cs,而不是**/*.cs - 补救措施:将
<EnableDefaultItems>false</EnableDefaultItems>作为最后手段(会失去 SDK 默认项)——请先优先尝试上述两个选项 - 检查:在诊断日志中搜索 Compile 项 → 如果 Compile 项包含意外文件,说明 glob 范围过宽
导入链分析
- 深层导入链(超过 20 层)会拖慢评估
- 每次导入都会产生文件 I/O、解析和评估开销
- 常见原因:NuGet 包添加了 .props/.targets、框架 SDK 导入、Directory.Build 链
- 诊断:查看
/pp输出 → 搜索<!-- Importing注释以查看导入树 - 补救措施(仅当导入链的开销经测量确实较大时):尽可能减少传递性包导入,合并导入
多次评估
- 同一项目被评估多次 = 浪费工作
- 常见原因:被多个其他项目引用,且全局属性各不相同
- 每组唯一的全局属性都会触发单独评估
- 诊断:
grep 'Evaluation started.*ProjectName' full.log→ 如果结果数大于 1,请检查是否存在不同的全局属性 - 修复:规范化全局属性,使用图构建(
/graph)
TreatAsLocalProperty
- 防止属性值通过 MSBuild 任务传递到子项目
- 过度使用:声明许多 TreatAsLocalProperty 条目会增加评估开销
- 正确用法:仅在确实需要覆盖继承属性时使用
属性函数开销
- 属性函数会在评估期间执行
- 大多数开销很小(字符串操作)
- 开销较大:在评估期间执行
$([System.IO.File]::ReadAllText(...))——每次评估都会读取文件 - 开销较大:网络调用、繁重计算
- 规则:属性函数应快速执行且无副作用
优化检查清单
- 检查预处理输出大小:
dotnet msbuild -pp:full.xml - 验证评估次数:每个项目、每个 TFM 应为 1 次
- 将大型目录排除在 glob 之外
- 避免在评估期间于属性函数中执行文件 I/O
- 尽量减少导入深度
- 使用图构建以减少重复评估
- 检查是否存在不必要的 UsingTask 声明
评论
支持 Markdown