SkillAgentSearch skills...

build-perf-baseline

Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before/after measurements (cold, warm, no-op scenarios), applying optimization strategies like static graph builds, artifacts output, and dependency graph trimming.

Install / Use

npx skills add dotnet/skills --skill build-perf-baseline

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

87/100

Supported Platforms

Universal

Tags

Our assessment of build-perf-baseline

build-perf-baseline scores 87/100 on our quality scale, 860th of 2,398 Development & Engineering skills we index (top 36%).

Its SKILL.md is 11 KB long, well organised into 64 sections with 21 code examples: a thorough specification that gives an agent plenty to work with.

With 5,471 GitHub stars, it is one of the more widely adopted skills in the catalogue.

Substance
29/30
Structure
20/20
Description
15/15
Adoption
16/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 2 days ago, so build-perf-baseline is actively maintained.
  • It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

build-perf-baseline compared with similar skills

All 4 of these similar skills score higher than build-perf-baseline; compare them before choosing.

SkillScoreStarsUpdatedFormat
build-perf-baseline (this skill)by dotnet875.5k2d agoSKILL.md
ai-job-searchby MadsLorentzen10044.0k5d agoCLAUDE.md
claude-howtoby luongnv8910041.7ktodayCLAUDE.md
algorithmic-artby anthropics100177.9k4d agoSKILL.md
pptxby anthropics100177.9k4d agoSKILL.md

Frequently asked questions

How do I install build-perf-baseline?
Run npx skills add dotnet/skills --skill build-perf-baseline. The install tabs above show the steps for each supported agent.
Which AI agents does build-perf-baseline work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is build-perf-baseline safe to use?
It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is build-perf-baseline still maintained?
The repository was last updated 2 days ago, so build-perf-baseline is actively maintained.

name: build-perf-baseline description: "Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before/after measurements (cold, warm, no-op scenarios), applying optimization strategies like static graph builds, artifacts output, and dependency graph trimming. Start here before diving into build-perf-diagnostics, incremental-build, or build-parallelism. DO NOT USE FOR: non-MSBuild build systems, detailed bottleneck analysis (use build-perf-diagnostics after baselining)." license: MIT

Build Performance Baseline & Optimization

Overview

Before optimizing a build, you need a baseline. Without measurements, optimization is guesswork. This skill covers how to establish baselines and apply systematic optimization techniques.

Related skills:

  • build-perf-diagnostics — binlog-based bottleneck identification
  • incremental-build — Inputs/Outputs and up-to-date checks
  • build-parallelism — parallel and graph build tuning
  • eval-performance — glob and import chain optimization

Step 1: Establish a Performance Baseline

Measure three scenarios to understand where time is spent:

Cold Build (First Build)

No previous build output exists. Measures the full end-to-end time including restore, compilation, and all targets.

# 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

Warm Build (Incremental Build)

Build output exists, some files have changed. Measures how well incremental build works.

# 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

No-Op Build (Nothing Changed)

Build output exists, nothing has changed. This should be nearly instant. If it's slow, incremental build is broken.

# Build once to populate outputs
dotnet build -m

# Rebuild immediately without changes
dotnet build /bl:noop-build.binlog -m

What Good Looks Like

| 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" |

Red flags:

  • No-op build > 30 seconds → incremental build is broken (see incremental-build skill)
  • Warm build recompiles everything → project dependency chain forces full rebuild
  • Cold build has long restore → NuGet cache issues

Recording Baselines

Record baselines in a structured way before and after optimization:

| Scenario    | Before  | After   | Improvement |
|-------------|---------|---------|-------------|
| Cold build  | 2m 15s  |         |             |
| Warm build  | 1m 40s  |         |             |
| No-op build | 45s     |         |             |

Step 2: Artifacts Output Layout

The UseArtifactsOutput feature (introduced in .NET 8) changes the output directory structure to avoid bin/obj clash issues and enable better caching.

Enabling Artifacts Output

<!-- Directory.Build.props -->
<PropertyGroup>
  <UseArtifactsOutput>true</UseArtifactsOutput>
</PropertyGroup>

Before vs After

# 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/...

Benefits

  • No bin/obj clash: Each project+configuration gets a unique path automatically
  • Easier to cache: Single artifacts/ directory to cache/restore in CI
  • Cleaner .gitignore: Just ignore artifacts/
  • Multi-targeting safe: Each TFM gets its own subdirectory

Customizing

<!-- Change the artifacts root -->
<PropertyGroup>
  <ArtifactsPath>$(MSBuildThisFileDirectory)output</ArtifactsPath>
</PropertyGroup>

Step 3: Deterministic Builds

Deterministic builds produce byte-for-byte identical output given the same inputs. This is essential for build caching and reproducibility.

Enabling Deterministic Builds

<!-- 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>

What Deterministic Affects

  • Removes timestamps from PE headers
  • Uses consistent file paths in PDBs
  • Produces identical output for identical input

Why It Matters for Performance

  • Build caching: If outputs are deterministic, you can cache and reuse them across builds and machines
  • CI optimization: Skip rebuilding unchanged projects by comparing inputs
  • Distributed builds: Safe to cache compilation results in shared storage

Step 4: Dependency Graph Trimming

Reducing unnecessary project references shortens the critical path and reduces what gets built.

Audit the Dependency Graph

# 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

Techniques

Remove Redundant Transitive References

<!-- 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>

Build-Order-Only References

When you need a project to build before yours but don't need its assembly output:

<!-- Only ensures build order, doesn't reference the output assembly -->
<ProjectReference Include="..\CodeGen\CodeGen.csproj"
                  ReferenceOutputAssembly="false" />

Prevent Transitive Flow

When a dependency is an internal implementation detail that shouldn't flow to consumers:

<!-- Don't expose this dependency transitively -->
<ProjectReference Include="..\InternalHelpers\InternalHelpers.csproj"
                  PrivateAssets="all" />

Disable Transitive Project References

For explicit-only dependency management (extreme measure for very large repos):

<PropertyGroup>
  <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
</PropertyGroup>

Caution: This requires all dependencies to be listed explicitly. Only use in large repos where transitive closure is causing excessive rebuilds.


Step 5: Static Graph Builds (/graph)

Static graph mode evaluates the entire project graph before building, enabling better scheduling and isolation.

Enabling Graph Build

# Single invocation
dotnet build /graph

# With binary log for analysis
dotnet build /graph /bl:graph-build.binlog

Benefits

  • Better parallelism: MSBuild knows the full graph upfront and can schedule optimally
  • Build isolation: Each project builds in isolation (no cross-project state leakage)
  • Caching potential: With isolation, individual project results can be cached

When to Use

| Scenario | Recommendation | |----------|---------------| | Large multi-project solution (20+ projects) | ✅ Try /graph — may see significant parallelism gains | | 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 |

Troubleshooting Graph Build

Graph build requires that all ProjectReference items are statically determinable (no dynamic references computed in targets). If graph build fails:

error MSB4260: Project reference "..." could not be resolved with static graph.

Fix: Ensure all ProjectReference items are declared in <ItemGroup> outside of targets (not dynamically computed inside <Target> blocks).


Step 6: Parallel Build Tuning

MaxCpuCount

# 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.sln

Identifying Parallelism Bottlenecks

In a binlog, look for:

  • Long sequential chains: Projects that must build one after another due to dependencies
  • Uneven load: Some build nodes idle while others are overloaded
  • Single-project bottleneck: One large project on the critical path that blocks everything

Use grep 'Target Performance Summary' -A 30 full.log in binlog analysis to see build node utilization.

Reducing the Critical Path

The critical path is the longest chain of dependent projects. To shorten it:

  1. Break large projects into smaller ones that can build in parallel
  2. Remove unnecessary ProjectReferences (see Step 5)
  3. Use ReferenceOutputAssembly="false" for build-order-only dependencies
  4. Move shared code to a base library that builds first, then parallelize consumers

Step 7: Additional Quick Wins

Separate Restore from Build

# In CI, restore once then build without restore
dotnet restore
dotnet build --no-restore -m
dotnet test --no-build

Skip Unnecessary Targets

# Skip building documentation
dotnet build /p:GenerateDocumentationFile=false

# Skip analyzers during development (not for CI!)
dotnet build /p:RunAnalyzers=false

Use Project-Level Filtering

# 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 project

Binary Log for All Investigations

Always start with a binlog:

dotnet build /bl:perf.binlog -m

Then use the build-perf-diagnostics skill and binlog tools for systematic bottleneck identification.


Optimization Decision Tree

Is 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

Related Skills

View on GitHub
GitHub Stars5.5k
CategoryDevelopment
Updated2d ago
Forks418

Languages

C#

Trust signals

100/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

No cautions