
Skill
eval-performance
diagnose and improve MSBuild evaluation performance
Description
Guide for diagnosing and improving MSBuild project evaluation performance. USE FOR: builds slow before any compilation starts, high evaluation time in binlog analysis, expensive glob patterns walking large directories (node_modules, .git, bin/obj), deep import chains (>20 levels), preprocessed output >10K lines indicating heavy evaluation, property functions with file I/O ($([System.IO.File]::ReadAllText(...))), multiple evaluations per project. Covers the 5 MSBuild evaluation phases, glob optimization via DefaultItemExcludes, import chain analysis with /pp preprocessing. DO NOT USE FOR: compilation-time slowness (use build-perf-diagnostics), incremental build issues (use incremental-build), non-MSBuild build systems.
SKILL.md
Diagnosing MSBuild Evaluation Performance
Evaluation is the work MSBuild does before any target runs — reading project files, processing imports, expanding globs. This skill helps you find and confirm evaluation bottlenecks. Measure first; recommend a change only when a measurement proves it is warranted.
Confirm the problem before changing anything
Engage only when evaluation is measurably the bottleneck. Do NOT act when:
- The slowness is during compilation or target execution, not evaluation.
That is not an evaluation problem — use
build-perf-diagnosticsinstead. - The complaint is "rebuilds too much" / incremental build. Use
incremental-buildinstead. - You have no measurement. If no binlog or timing summary shows evaluation is slow, gather one first (see below). Do not guess from reading project files.
- A pattern below appears but evaluation is already fast. Broad globs, deep
imports, or
EnableDefaultItemsare only worth flagging when the numbers show they cost real time. A project that evaluates quickly needs no change.
When a pattern is present but unmeasured, report it as an observation and let the user decide — do not rewrite working configuration to match a "best practice" without evidence it costs measurable evaluation time. Prefer the smallest, most targeted change; never disable SDK defaults as a first move.
MSBuild Evaluation Phases
For a comprehensive overview of MSBuild's evaluation and execution model, see Build process overview.
- Initial properties: environment variables, global properties, reserved properties
- Imports and property evaluation: process
<Import>, evaluate<PropertyGroup>top-to-bottom - Item definition evaluation:
<ItemDefinitionGroup>metadata defaults - Item evaluation:
<ItemGroup>withInclude,Remove,Update, glob expansion - 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
Primary: binlog MCP (preferred)
Use the binlog MCP server (Microsoft.AITools.BinlogMcp, exposed under the binlog MCP namespace) to analyze evaluation performance:
- Use the evaluations tool to list all evaluations and their durations
- Use evaluation_global_properties to check for multiple evaluations with differing global properties
- Use evaluation_properties to inspect evaluated properties for a specific project+TFM
- Use imports tool to analyze the import chain depth and structure
- Use properties tool to check for expensive property function evaluations
Fallback: text-log replay and preprocessing (when MCP is unavailable)
Using binlog
- Replay the binlog:
dotnet msbuild build.binlog -noconlog -fl -flp:v=diag;logfile=full.log - Search for evaluation events:
grep -i 'Evaluation started\|Evaluation finished' full.log - Multiple evaluations for the same project = overbuilding
- 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
Only pursue these remedies once a measurement shows item evaluation is slow and the globs are the cause; a custom glob that isn't walking large trees is fine.
- Globs like
**/*.cswalk 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 - Remedy: use
<DefaultItemExcludes>to exclude large directories - Remedy: be specific with glob paths:
src/**/*.csinstead of**/*.cs - Remedy: use
<EnableDefaultItems>false</EnableDefaultItems>only as a last resort (loses SDK defaults) — prefer the two options above first - 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:
/ppoutput → search for<!-- Importingcomments to see import tree - Remedy (only if the chain is measurably costly): 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
More skills from the skills repository
View all 99 skillsanalyzing-dotnet-performance
analyze .NET code for performance anti-patterns
Jul 12.NETCode AnalysisDebuggingPerformanceandroid-tombstone-symbolication
symbolicate .NET runtime frames in Android tombstones
Jul 12.NETAndroidDebuggingMicrosoftapple-crash-symbolication
symbolicate .NET runtime frames in crash logs
Jul 12.NETDebuggingiOSmacOS +1assertion-quality
evaluate assertion quality in test suites
Jul 12Code AnalysisQATestingauthor-component
create and review Blazor components
Jul 15.NETBlazorC#UI Components +1binlog-failure-analysis
analyze MSBuild binary logs
Jul 12Code AnalysisDebuggingMicrosoft
More from .NET (Microsoft)
View publishermultithreaded-task-migration
migrate MSBuild tasks to multithreaded mode
msbuild
Jul 22.NETEngineeringPerformancebinlog-generation
generate MSBuild binary logs for diagnostics
skills
Jul 19BuildDebuggingEngineeringbuild-parallelism
optimize MSBuild build parallelism
skills
Jul 19.NETEngineeringPerformancebuild-perf-baseline
establish and optimize build performance baselines
skills
Jul 12EngineeringMonitoringPerformanceTestingbuild-perf-diagnostics
diagnose MSBuild build performance bottlenecks
skills
Jul 12.NETDebuggingEngineeringPerformancecheck-bin-obj-clash
detect MSBuild output path conflicts
skills
Jul 19DebuggingEngineeringQA