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.
Instalación
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
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