skills/dotnet/skills/eval-performance

eval-performance

SKILL.md

MSBuild Evaluation Phases

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.

Diagnosing Evaluation Performance

Using 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

Using /pp (preprocess)

  • 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

Using /clp:PerformanceSummary

  • Add to build command for timing breakdown
  • Shows evaluation time separately from target/task execution

Expensive Glob Patterns

  • 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

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

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)

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

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

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
Weekly Installs
22
Repository
dotnet/skills
GitHub Stars
464
First Seen
6 days ago
Installed on
kimi-cli21
gemini-cli21
amp21
cline21
github-copilot21
codex21