Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target/Task Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.
التثبيت
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target/Task Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.
license
MIT
Performance Analysis Methodology
Generate a binlog: dotnet build /bl:{} -m
Use the binlog MCP server (Microsoft.AITools.BinlogMcp, exposed under the binlog MCP namespace) which is bundled with this plugin
Alternate flow when MCP is unavailable: binlog replay to text logs
Generate a binlog: dotnet build /bl:{} -m
Replay to diagnostic log with performance summary:
Build duration: what's "normal" — small project <10s, medium <60s, large <5min
Node utilization: ideal is >80% active time across nodes. Low utilization = serialization bottleneck
Single target domination: if one target is >50% of build time, investigate
Analyzer time vs compile time: analyzers should be <30% of Csc task time. If higher, consider removing expensive analyzers
RAR time: ResolveAssemblyReference >5s is concerning. >15s is pathological
Common Bottlenecks
1. ResolveAssemblyReference (RAR) Slowness
Symptoms: RAR taking >5s per project
Root causes: too many assembly references, network-based reference paths, large assembly search paths
Fixes: reduce reference count, use <DesignTimeBuild>false</DesignTimeBuild> for RAR-heavy analysis, set <ResolveAssemblyReferencesSilent>true</ResolveAssemblyReferencesSilent> for diagnostic
Advanced: <DesignTimeBuild> and <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
Key insight: RAR runs unconditionally even on incremental builds because users may have installed targeting packs or GACed assemblies (see dotnet/msbuild#2015). With .NET Core micro-assemblies, the reference count is often very high.
Reduce transitive references: Set <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> to avoid pulling in the full transitive closure (note: projects may need to add direct references for any types they consume). Use ReferenceOutputAssembly="false" on ProjectReferences that are only needed at build time (not API surface). Trim unused PackageReferences.
2. Roslyn Analyzers and Source Generators
Symptoms: Csc task takes much longer than expected for file count (>2× clean compile time)
Diagnosis: Check the Task Performance Summary in the replayed log for Csc task time; grep for analyzer timing messages; compare Csc duration with and without analyzers (/p:RunAnalyzers=false)
Fixes:
Conditionally disable in dev: <RunAnalyzers Condition="'$(ContinuousIntegrationBuild)' != 'true'">false</RunAnalyzers>
Remove genuinely redundant analyzers from inner loop
Severity config in .editorconfig for less critical rules
Key principle: Preserve analyzer enforcement in CI. Never just "remove" analyzers — configure them conditionally.
GlobalPackageReference: Analyzers added via GlobalPackageReference in Directory.Packages.props apply to ALL projects. Consider if test projects need the same analyzer set as production code.
EnforceCodeStyleInBuild: When set to true in Directory.Build.props, forces code-style analysis on every build. Should be conditional on CI environment (ContinuousIntegrationBuild) to avoid slowing dev inner loop.
Symptoms: Performance summary shows most build time concentrated in a single project; diagnostic log shows idle nodes while one works
Common culprits: targets without proper dependency declaration, single project on critical path
Fixes: split large projects, optimize the critical path project, ensure proper BuildInParallel
4. Excessive File I/O (Copy tasks)
Symptoms: Copy task shows high aggregate time
Root causes: copying thousands of files, copying across network drives, Copy task unintentionally running once per item (per-file) instead of as a single batch (see dotnet/msbuild#12884)
Fixes: use hardlinks (<CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>true</CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>), reduce CopyToOutputDirectory items, use <UseCommonOutputDirectory>true</UseCommonOutputDirectory> when appropriate, set <SkipCopyUnchangedFiles>true</SkipCopyUnchangedFiles>, consider --artifacts-path (.NET 8+) for centralized output layout
Dev Drive: On Windows, switching to a Dev Drive (ReFS with copy-on-write and reduced Defender scans) can significantly reduce file I/O overhead for Copy-heavy builds. Recommend for both dev machines and self-hosted CI agents.
5. Evaluation Overhead
Symptoms: build starts slow before any compilation
Root causes: complex Directory.Build.props, wildcard globs scanning large directories, NuGetSdkResolver overhead (adds 180-400ms per project evaluation even when restored — see dotnet/msbuild#4025)
Fixes: reduce Directory.Build.props complexity, use <EnableDefaultItems>false</EnableDefaultItems> for legacy projects with explicit file lists, avoid NuGet-based SDK resolvers if possible
See: eval-performance skill for detailed guidance
6. NuGet Restore in Build
Symptoms: restore runs every build even when unnecessary
Fixes:
Separate restore from build: dotnet restore then dotnet build --no-restore
Enable static graph evaluation: <RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation> in Directory.Build.props — can save significant time in large builds (results are workload-dependent)
7. Large Project Count and Graph Shape
Symptoms: many small projects, each takes minimal time but overhead adds up; deep dependency chains serialize the build
Consider: project consolidation, or use /graph mode for better scheduling
Graph shape matters: a wide dependency graph (few levels, many parallel branches) builds faster than a deep one (many levels, serialized). Refactoring from deep to wide can yield significant improvements in both clean and incremental build times.
Actions: look for unnecessary project dependencies, consider splitting a bottleneck project into two, or merging small leaf projects