본문으로 건너뛰기
cccskills
무료GitHub에 공개

build-perf-diagnostics

Diagnose MSBuild bottlenecks from binlog timelines and Target/Task Performance Summaries after build-perf-baseline establishes measurements. USE FOR: unknown build slowness, RAR >5s, analyzers >30% of Csc, one target >50% of build time, node utilization <80%, excessive Copy tasks, finding restore overhead. DO NOT USE FOR: known repeated-restore fixes (use eliminate-redundant-restores), initial baselines (use build-perf-baseline), incremental rebuilds (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild builds.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(2)

  • SKILL.md10.2 KB
  • manifest.json137 B

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Performance Analysis Methodology

  1. Generate a binlog: dotnet build /bl:{} -m
  2. 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

  1. Generate a binlog: dotnet build /bl:{} -m
  2. Replay to diagnostic log with performance summary:
    dotnet msbuild build.binlog -noconlog -fl "-flp:v=diag;logfile=full.log;performancesummary"
    
  3. Read the performance summary (at the end of full.log):
    grep "Target Performance Summary\|Task Performance Summary" -A 50 full.log
    
  4. Find expensive targets and tasks: The PerformanceSummary section lists all targets/tasks sorted by cumulative time
  5. Check for node utilization: grep for scheduling and node messages
    grep -i "node.*assigned\|building with\|scheduler" full.log | head -30
    
  6. Check analyzers: grep for analyzer timing
    grep -i "analyzer.*elapsed\|Total analyzer execution time\|CompilerAnalyzerDriver" full.log
    

Key Metrics and Thresholds

  • 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>
    • Per-configuration: <RunAnalyzers Condition="'$(Configuration)' == 'Debug'">false</RunAnalyzers>
    • Code-style only: <EnforceCodeStyleInBuild Condition="'$(ContinuousIntegrationBuild)' == 'true'">true</EnforceCodeStyleInBuild>
    • 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.

3. Serialization Bottlenecks (Single-threaded targets)

  • 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)
  • See: eliminate-redundant-restores skill when several pipeline or script steps each restore, or before adding --no-restore (it fails with NETSDK1004/NETSDK1047 when no earlier restore covers the step)

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

Using Binlog Replay for Performance Analysis

Step-by-step workflow using text log replay:

  1. Replay with performance summary:
    dotnet msbuild build.binlog -noconlog -fl "-flp:v=diag;logfile=full.log;performancesummary"
    
  2. Read target/task performance summaries (at the end of full.log):
    grep "Target Performance Summary\|Task Performance Summary" -A 50 full.log
    
    This shows all targets and tasks sorted by cumulative time — equivalent to finding expensive targets/tasks.
  3. Find per-project build times:
    grep "done building project\|Project Performance Summary" full.log
    
  4. Check parallelism (multi-node scheduling):
    grep -i "node.*assigned\|RequiresLeadingNewline\|Building with" full.log | head -30
    
  5. Check analyzer overhead:
    grep -i "Total analyzer execution time\|analyzer.*elapsed\|CompilerAnalyzerDriver" full.log
    
  6. Drill into a specific slow target:
    grep 'Target "CoreCompile"\|Target "ResolveAssemblyReferences"' full.log
    

Quick Wins Checklist

  • Use /maxcpucount (or -m) for parallel builds
  • Separate restore from build (dotnet restore then dotnet build --no-restore)
  • Enable static graph restore (<RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation>)
  • Enable hardlinks for Copy (<CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>true</CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>)
  • Disable analyzers conditionally in dev inner loop: <RunAnalyzers Condition="'$(ContinuousIntegrationBuild)' != 'true'">false</RunAnalyzers>
  • Enable reference assemblies (<ProduceReferenceAssembly>true</ProduceReferenceAssembly>)
  • Check for broken incremental builds (see incremental-build skill)
  • Check for bin/obj clashes (see check-bin-obj-clash skill)
  • Use graph build (/graph) for multi-project solutions
  • Use --artifacts-path (.NET 8+) for centralized output layout
  • Enable Dev Drive (ReFS) on Windows dev machines and self-hosted CI

Impact Categorization

When reporting findings, categorize by impact to help prioritize fixes:

  • 🔴 HIGH IMPACT (do first): Items consuming >10% of total build time, or a single target >50% of build time
  • 🟡 MEDIUM IMPACT: Items consuming 2-10% of build time
  • 🟢 QUICK WINS: Easy changes with modest impact (e.g., property flags in Directory.Build.props)

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

managedcode/dotnet-skills4902026년 10월 17일 업데이트

Use a repo-root `.editorconfig` to configure free .NET analyzer and style rules. Use when a .NET repo needs rule severity, code-style options, section layout, or analyzer ownership made explicit. USE FOR: the repo needs a root .editorconfig; analyzer severity and style ownership are unclear; the team wants one source of truth for rule configuration. DO NOT USE FOR: choosing analyzers with no config change; formatting-only execution with no config ownership question. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

managedcode/dotnet-skills4902026년 10월 17일 업데이트

Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I/O with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

managedcode/dotnet-skills4902026년 10월 17일 업데이트

Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java/Kotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

managedcode/dotnet-skills4902026년 10월 17일 업데이트

Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift/Objective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

managedcode/dotnet-skills4902026년 10월 17일 업데이트

Design or review .NET solution architecture across modular monoliths, clean architecture, vertical slices, microservices, DDD, CQRS, and cloud-native boundaries without over-engineering. USE FOR: .NET architecture choices; layer and domain boundary review; service decomposition; clean architecture, vertical slice, DDD, CQRS, and modular monolith decisions. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

managedcode/dotnet-skills4902026년 10월 17일 업데이트

managedcode의 스킬 모두 보기

이 스킬의 문제 신고