Loading...
Loading...
Establish build performance baselines and apply systematic optimization techniques. Only activate in MSBuild/.NET build context. Use when diagnosing slow builds, establishing before/after measurements, or applying advanced optimization strategies like MSBuild Server, static graph builds, artifacts output, and dependency graph trimming. Start here before diving into specific optimizations from build-perf-diagnostics, incremental-build, or build-parallelism skills. DO NOT use for non-MSBuild build systems.
npx skill4agent add dotnet/skills build-perf-baselinebuild-perf-diagnosticsincremental-buildbuild-parallelismeval-performance# Clean everything first
dotnet clean
# Remove bin/obj to truly start fresh
Get-ChildItem -Recurse -Directory -Include bin,obj | Remove-Item -Recurse -Force
# OR on Linux/macOS:
# find . -type d \( -name bin -o -name obj \) -exec rm -rf {} +
# Measure cold build
dotnet build /bl:cold-build.binlog -m# Build once to populate outputs
dotnet build -m
# Make a small change (touch one .cs file)
# Then rebuild
dotnet build /bl:warm-build.binlog -m# Build once to populate outputs
dotnet build -m
# Rebuild immediately without changes
dotnet build /bl:noop-build.binlog -m| Scenario | Expected Behavior |
|---|---|
| Cold build | Full compilation, all targets run. This is your absolute baseline |
| Warm build | Only changed projects recompile. Time proportional to change scope |
| No-op build | < 5 seconds for small repos, < 30 seconds for large repos. All compilation targets should report "Skipping target — all outputs up-to-date" |
incremental-build| Scenario | Before | After | Improvement |
|-------------|---------|---------|-------------|
| Cold build | 2m 15s | | |
| Warm build | 1m 40s | | |
| No-op build | 45s | | |# Enabled by default in .NET 8+ but can be forced
dotnet build /p:UseSharedCompilation=truedotnet build-server# Check if the server is running
dotnet build-server status
# Shut down all build servers (useful when debugging)
dotnet build-server shutdowndotnet build-server shutdown
dotnet buildUseArtifactsOutput<!-- Directory.Build.props -->
<PropertyGroup>
<UseArtifactsOutput>true</UseArtifactsOutput>
</PropertyGroup># Traditional layout (before)
src/
MyLib/
bin/Debug/net8.0/MyLib.dll
obj/Debug/net8.0/...
MyApp/
bin/Debug/net8.0/MyApp.dll
# Artifacts layout (after)
artifacts/
bin/MyLib/debug/MyLib.dll
bin/MyApp/debug/MyApp.dll
obj/MyLib/debug/...
obj/MyApp/debug/...artifacts/artifacts/<!-- Change the artifacts root -->
<PropertyGroup>
<ArtifactsPath>$(MSBuildThisFileDirectory)output</ArtifactsPath>
</PropertyGroup><!-- Directory.Build.props -->
<PropertyGroup>
<!-- Enabled by default in .NET SDK projects since SDK 2.0+ -->
<Deterministic>true</Deterministic>
<!-- For full reproducibility, also set: -->
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup># Visualize the dependency graph
dotnet build /bl:graph.binlog
# In the binlog, check project references and build times
# Look for projects that are referenced but could be trimmed<!-- BAD: Utils is already referenced transitively via Core -->
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
<ProjectReference Include="..\Utils\Utils.csproj" />
</ItemGroup>
<!-- GOOD: Let transitive references flow automatically -->
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup><!-- Only ensures build order, doesn't reference the output assembly -->
<ProjectReference Include="..\CodeGen\CodeGen.csproj"
ReferenceOutputAssembly="false" /><!-- Don't expose this dependency transitively -->
<ProjectReference Include="..\InternalHelpers\InternalHelpers.csproj"
PrivateAssets="all" /><PropertyGroup>
<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
</PropertyGroup>/graph# Single invocation
dotnet build /graph
# With binary log for analysis
dotnet build /graph /bl:graph-build.binlog| Scenario | Recommendation |
|---|---|
| Large multi-project solution (20+ projects) | ✅ Try |
| Small solution (< 5 projects) | ❌ Overhead of graph evaluation outweighs benefits |
| CI builds | ✅ Graph builds are more predictable and parallelizable |
| Local development | ⚠️ Test both — may or may not help depending on project structure |
ProjectReferenceerror MSB4260: Project reference "..." could not be resolved with static graph.ProjectReference<ItemGroup><Target># Use all available cores (default in dotnet build)
dotnet build -m
# Specify explicit core count (useful for CI with shared agents)
dotnet build -m:4
# MSBuild.exe syntax
msbuild /m:8 MySolution.slngrep 'Target Performance Summary' -A 30 full.logReferenceOutputAssembly="false"# In CI, restore once then build without restore
dotnet restore
dotnet build --no-restore -m
dotnet test --no-build# Skip building documentation
dotnet build /p:GenerateDocumentationFile=false
# Skip analyzers during development (not for CI!)
dotnet build /p:RunAnalyzers=false# Build only the project you're working on (and its dependencies)
dotnet build src/MyApp/MyApp.csproj
# Don't build the entire solution if you only need one projectdotnet build /bl:perf.binlog -mbuild-perf-diagnosticsIs your no-op build slow (> 10s per project)?
├── YES → See `incremental-build` skill (fix Inputs/Outputs)
└── NO
Is your cold build slow?
├── YES
│ Is restore slow?
│ ├── YES → Optimize NuGet restore (use lock files, configure local cache)
│ └── NO
│ Is compilation slow?
│ ├── YES
│ │ Are analyzers/generators slow?
│ │ ├── YES → See `build-perf-diagnostics` skill
│ │ └── NO → Check parallelism, graph build, critical path (this skill + `build-parallelism`)
│ └── NO → Check custom targets (binlog analysis via `build-perf-diagnostics`)
└── NO
Is your warm build slow?
├── YES → Projects rebuilding unnecessarily → check `incremental-build` skill
└── NO → Build is healthy! Consider graph build or UseArtifactsOutput for further gains