eval-performance

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MSBuild Evaluation Phases

MSBuild 评估阶段

For a comprehensive overview of MSBuild's evaluation and execution model, see Build process overview.
  1. Initial properties: environment variables, global properties, reserved properties
  2. Imports and property evaluation: process
    <Import>
    , evaluate
    <PropertyGroup>
    top-to-bottom
  3. Item definition evaluation:
    <ItemDefinitionGroup>
    metadata defaults
  4. Item evaluation:
    <ItemGroup>
    with
    Include
    ,
    Remove
    ,
    Update
    , glob expansion
  5. UsingTask evaluation: register custom tasks
Key insight: evaluation happens BEFORE any targets run. Slow evaluation = slow build start even when nothing needs compiling.
如需全面了解MSBuild的评估和执行模型,请参阅构建流程概述
  1. 初始属性:环境变量、全局属性、保留属性
  2. 导入与属性评估:处理
    <Import>
    ,从上到下评估
    <PropertyGroup>
  3. 项定义评估
    <ItemDefinitionGroup>
    元数据默认值
  4. 项评估:包含
    Include
    Remove
    Update
    、glob展开的
    <ItemGroup>
  5. UsingTask评估:注册自定义任务
核心要点:评估发生在所有目标运行之前。评估缓慢 = 即使没有任何内容需要编译,构建启动也会很慢。

Diagnosing Evaluation Performance

诊断评估性能

Using binlog

使用binlog

  1. Replay the binlog:
    dotnet msbuild build.binlog -noconlog -fl -flp:v=diag;logfile=full.log
  2. Search for evaluation events:
    grep -i 'Evaluation started\|Evaluation finished' full.log
  3. Multiple evaluations for the same project = overbuilding
  4. Look for "Project evaluation started/finished" messages and their timestamps
  1. 重放binlog:
    dotnet msbuild build.binlog -noconlog -fl -flp:v=diag;logfile=full.log
  2. 搜索评估事件:
    grep -i 'Evaluation started\|Evaluation finished' full.log
  3. 同一个项目多次评估 = 过度构建
  4. 查找“Project evaluation started/finished”消息及其时间戳

Using /pp (preprocess)

使用/pp(预处理)

  • dotnet msbuild -pp:full.xml MyProject.csproj
  • Shows the fully expanded project with ALL imports inlined
  • Use to understand: what's imported, import depth, total content volume
  • Large preprocessed output (>10K lines) = heavy evaluation
  • dotnet msbuild -pp:full.xml MyProject.csproj
  • 展示完全展开的项目,所有导入都已内联
  • 用于了解:导入内容、导入深度、总内容量
  • 预处理输出过大(>10K行)= 评估负担重

Using /clp:PerformanceSummary

使用/clp:PerformanceSummary

  • Add to build command for timing breakdown
  • Shows evaluation time separately from target/task execution
  • 添加到构建命令中以获取耗时拆分
  • 单独展示评估时间,与目标/任务执行时间区分开

Expensive Glob Patterns

高开销Glob模式

  • Globs like
    **/*.cs
    walk the entire directory tree
  • Default SDK globs are optimized, but custom globs may not be
  • Problem: globbing over
    node_modules/
    ,
    .git/
    ,
    bin/
    ,
    obj/
    — millions of files
  • Fix: use
    <DefaultItemExcludes>
    to exclude large directories
  • Fix: be specific with glob paths:
    src/**/*.cs
    instead of
    **/*.cs
  • Fix: use
    <EnableDefaultItems>false</EnableDefaultItems>
    only as last resort (lose SDK defaults)
  • Check: grep for Compile items in the diagnostic log → if Compile items include unexpected files, globs are too broad
  • 类似
    **/*.cs
    的glob会遍历整个目录树
  • 默认SDK的glob已经过优化,但自定义glob可能没有
  • 问题场景:对
    node_modules/
    .git/
    bin/
    obj/
    等包含数百万文件的目录做glob匹配
  • 修复方案:使用
    <DefaultItemExcludes>
    排除大体积目录
  • 修复方案:使用更明确的glob路径:用
    src/**/*.cs
    代替
    **/*.cs
  • 修复方案:仅万不得已时使用
    <EnableDefaultItems>false</EnableDefaultItems>
    (会丢失SDK默认配置)
  • 检查方法:在诊断日志中搜索Compile项 → 如果Compile项包含预期外的文件,说明glob范围过宽

Import Chain Analysis

导入链分析

  • Deep import chains (>20 levels) slow evaluation
  • Each import: file I/O + parse + evaluate
  • Common causes: NuGet packages adding .props/.targets, framework SDK imports, Directory.Build chains
  • Diagnosis:
    /pp
    output → search for
    <!-- Importing
    comments to see import tree
  • Fix: reduce transitive package imports where possible, consolidate imports
  • 深层导入链(>20层)会拖慢评估速度
  • 每次导入都需要:文件I/O + 解析 + 评估
  • 常见原因:NuGet包添加.props/.targets文件、框架SDK导入、Directory.Build链
  • 诊断方法:查看/pp输出 → 搜索
    <!-- Importing
    注释查看导入树
  • 修复方案:尽可能减少传递包导入,合并导入

Multiple Evaluations

多次评估

  • A project evaluated multiple times = wasted work
  • Common causes: referenced from multiple other projects with different global properties
  • Each unique set of global properties = separate evaluation
  • Diagnosis:
    grep 'Evaluation started.*ProjectName' full.log
    → if count > 1, check for differing global properties
  • Fix: normalize global properties, use graph build (
    /graph
    )
  • 一个项目被多次评估 = 无用开销
  • 常见原因:被多个使用不同全局属性的其他项目引用
  • 每组唯一的全局属性 = 单独的一次评估
  • 诊断方法:
    grep 'Evaluation started.*ProjectName' full.log
    → 如果计数>1,检查是否存在不同的全局属性
  • 修复方案:统一全局属性,使用图构建(
    /graph

TreatAsLocalProperty

TreatAsLocalProperty

  • Prevents property values from flowing to child projects via MSBuild task
  • Overuse: declaring many TreatAsLocalProperty entries adds evaluation overhead
  • Correct use: only when you genuinely need to override an inherited property
  • 防止属性值通过MSBuild任务传递到子项目
  • 过度使用:声明大量TreatAsLocalProperty条目会增加评估开销
  • 正确用法:仅当你确实需要覆盖继承的属性时才使用

Property Function Cost

属性函数开销

  • Property functions execute during evaluation
  • Most are cheap (string operations)
  • Expensive:
    $([System.IO.File]::ReadAllText(...))
    during evaluation — reads file on every evaluation
  • Expensive: network calls, heavy computation
  • Rule: property functions should be fast and side-effect-free
  • 属性函数在评估阶段执行
  • 大部分开销很低(字符串操作)
  • 高开销场景:评估阶段执行
    $([System.IO.File]::ReadAllText(...))
    → 每次评估都会读取文件
  • 高开销场景:网络调用、大量计算
  • 规则:属性函数应该快速且无副作用

Optimization Checklist

优化清单

  • Check preprocessed output size:
    dotnet msbuild -pp:full.xml
  • Verify evaluation count: should be 1 per project per TFM
  • Exclude large directories from globs
  • Avoid file I/O in property functions during evaluation
  • Minimize import depth
  • Use graph build to reduce redundant evaluations
  • Check for unnecessary UsingTask declarations
  • 检查预处理输出大小:
    dotnet msbuild -pp:full.xml
  • 验证评估次数:每个项目每个TFM应为1次
  • 从glob中排除大体积目录
  • 评估阶段避免在属性函数中执行文件I/O
  • 最小化导入深度
  • 使用图构建减少冗余评估
  • 检查不必要的UsingTask声明