@cratis/pi 7.102.0 → 7.103.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
- package/package/corpus/hooks/README.md +11 -1
- package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
- package/package/corpus/rules/profiles.md +10 -1
- package/package/corpus/rules/pull-requests.md +2 -0
- package/package/corpus/rules/secrets.md +4 -0
- package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
- package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
- package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
- package/package/corpus/skills/binlog-generation/LICENSE +23 -0
- package/package/corpus/skills/binlog-generation/NOTICE +22 -0
- package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
- package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
- package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
- package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
- package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
- package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
- package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
- package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
- package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
- package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
- package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
- package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
- package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
- package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
- package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
- package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +26 -1
- package/package/corpus/skills/cratis-screenplay-projections/SKILL.md +17 -0
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +17 -0
- package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +8 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +1 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/fast-path.md +7 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +1 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +40 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/response-continuations.md +1 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/worked-flow.md +1 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +3 -2
- package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
- package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
- package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
- package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
- package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
- package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
- package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
- package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
- package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
- package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
- package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
- package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
- package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
- package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
- package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
- package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
- package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
- package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
- package/package/corpus/skills/dump-collect/LICENSE +23 -0
- package/package/corpus/skills/dump-collect/NOTICE +40 -0
- package/package/corpus/skills/dump-collect/SKILL.md +97 -0
- package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
- package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
- package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
- package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
- package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
- package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
- package/package/corpus/skills/extension-points/LICENSE +23 -0
- package/package/corpus/skills/extension-points/NOTICE +18 -0
- package/package/corpus/skills/extension-points/SKILL.md +248 -0
- package/package/corpus/skills/filter-syntax/LICENSE +23 -0
- package/package/corpus/skills/filter-syntax/NOTICE +17 -0
- package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
- package/package/corpus/skills/including-generated-files/LICENSE +23 -0
- package/package/corpus/skills/including-generated-files/NOTICE +17 -0
- package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
- package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
- package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
- package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
- package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
- package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
- package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
- package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
- package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
- package/package/corpus/skills/msbuild/LICENSE +23 -0
- package/package/corpus/skills/msbuild/NOTICE +55 -0
- package/package/corpus/skills/msbuild/SKILL.md +117 -0
- package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
- package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
- package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
- package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
- package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
- package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
- package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
- package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
- package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
- package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
- package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
- package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
- package/package/corpus/skills/orleans/LICENSE +21 -0
- package/package/corpus/skills/orleans/NOTICE +64 -0
- package/package/corpus/skills/orleans/SKILL.md +140 -0
- package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
- package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
- package/package/corpus/skills/orleans/references/examples.md +66 -0
- package/package/corpus/skills/orleans/references/grain-api.md +784 -0
- package/package/corpus/skills/orleans/references/grains.md +75 -0
- package/package/corpus/skills/orleans/references/hosting.md +71 -0
- package/package/corpus/skills/orleans/references/implementation.md +591 -0
- package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
- package/package/corpus/skills/orleans/references/patterns.md +811 -0
- package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
- package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
- package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
- package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
- package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
- package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
- package/package/corpus/skills/platform-detection/LICENSE +23 -0
- package/package/corpus/skills/platform-detection/NOTICE +17 -0
- package/package/corpus/skills/platform-detection/SKILL.md +230 -0
- package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
- package/package/corpus/skills/profiling/LICENSE +21 -0
- package/package/corpus/skills/profiling/NOTICE +50 -0
- package/package/corpus/skills/profiling/SKILL.md +144 -0
- package/package/corpus/skills/profiling/references/commands.md +439 -0
- package/package/corpus/skills/profiling/references/patterns.md +586 -0
- package/package/corpus/skills/profiling/references/profiling.md +190 -0
- package/package/corpus/skills/run-tests/LICENSE +23 -0
- package/package/corpus/skills/run-tests/NOTICE +19 -0
- package/package/corpus/skills/run-tests/SKILL.md +305 -0
- package/package/corpus/skills/target-authoring/LICENSE +23 -0
- package/package/corpus/skills/target-authoring/NOTICE +17 -0
- package/package/corpus/skills/target-authoring/SKILL.md +165 -0
- package/package/corpus/skills/vectorization/LICENSE +23 -0
- package/package/corpus/skills/vectorization/NOTICE +17 -0
- package/package/corpus/skills/vectorization/SKILL.md +189 -0
- package/package/profile-catalog.json +85 -1
- package/package.json +1 -1
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Diagnosing MSBuild Build Performance
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Performance Analysis Methodology
|
|
6
|
+
- Key Metrics and Thresholds
|
|
7
|
+
- Common Bottlenecks
|
|
8
|
+
- Using Binlog Replay for Performance Analysis
|
|
9
|
+
- Quick Wins Checklist
|
|
10
|
+
- Impact Categorization
|
|
11
|
+
|
|
12
|
+
Source: original build perf diagnostics skill.
|
|
13
|
+
|
|
14
|
+
## Performance Analysis Methodology
|
|
15
|
+
|
|
16
|
+
1. Establish the relevant cold, warm changed-input, or no-change
|
|
17
|
+
[baseline](build-perf-baseline.md). Keep `Rebuild` and ordinary `Build` distinct.
|
|
18
|
+
2. Use an existing matching binlog first. If one is missing, follow
|
|
19
|
+
[binlog generation](binlog-generation.md) without changing the reported scenario.
|
|
20
|
+
3. [Replay the binary log](binlog-failure-analysis.md#replay-a-binary-log) locally
|
|
21
|
+
to a diagnostic text log with a performance summary. The searches below assume
|
|
22
|
+
that log is named `full-01.log`, as in the replay command; use its actual path.
|
|
23
|
+
4. Find expensive projects, targets, and tasks, then inspect the corresponding
|
|
24
|
+
instance, execution reason, inputs, and dependency waits. Select one cause
|
|
25
|
+
for a controlled change, not every plausible optimization.
|
|
26
|
+
|
|
27
|
+
Performance summaries report cumulative/inclusive durations, not additive shares
|
|
28
|
+
of elapsed wall time or CPU usage. Parallel work overlaps; orchestration targets
|
|
29
|
+
and tasks can include child work and waits.
|
|
30
|
+
|
|
31
|
+
In particular, **`ResolveProjectReferences` can mostly represent waiting for
|
|
32
|
+
dependent builds**, while a yielded node performs other work. Do not optimize it
|
|
33
|
+
solely because it tops the target summary. Follow the dependencies to actual
|
|
34
|
+
`Csc`, `ResolveAssemblyReference`, `Copy`, or custom-task work. Even `MSBuild`
|
|
35
|
+
task duration can include child builds rather than exclusive self-time.
|
|
36
|
+
|
|
37
|
+
## Key Metrics and Thresholds
|
|
38
|
+
|
|
39
|
+
Use these as investigation hints, not universal budgets or proof of a cause:
|
|
40
|
+
|
|
41
|
+
- **Build duration:** historical rough examples are under 10 seconds for a small
|
|
42
|
+
workload, 60 seconds for a medium one, and 5 minutes for a large one. Compare
|
|
43
|
+
the repository's actual scenario and machine, not labels alone.
|
|
44
|
+
- **Node utilization:** activity below roughly 80% warrants checking dependency
|
|
45
|
+
waits, limited parallel work, and resource contention. Text-summary totals
|
|
46
|
+
alone cannot establish utilization.
|
|
47
|
+
- **Single-target domination:** investigate a target near or above 50% of elapsed
|
|
48
|
+
build time, but separate inclusive waits and nested work.
|
|
49
|
+
- **Analyzer versus compiler time:** around 30% or more of `Csc` time deserves
|
|
50
|
+
investigation. Analyzer timings can overlap; do not add them as wall time or
|
|
51
|
+
remove diagnostics solely to reach a percentage.
|
|
52
|
+
- **RAR time:** over 5 seconds per project, especially over 15 seconds, merits
|
|
53
|
+
examination of reference count, paths, and resolution work.
|
|
54
|
+
|
|
55
|
+
## Common Bottlenecks
|
|
56
|
+
|
|
57
|
+
### 1. ResolveAssemblyReference (RAR) Slowness
|
|
58
|
+
|
|
59
|
+
- **Symptoms:** substantial `ResolveAssemblyReference` task time, for example
|
|
60
|
+
over 5 seconds per affected project.
|
|
61
|
+
- **Root causes:** many assembly references, network-based paths, or expensive
|
|
62
|
+
assembly search paths.
|
|
63
|
+
- **Fixes:** remove demonstrated unused references/search paths and avoid
|
|
64
|
+
unnecessary network resolution, preserving all consumers.
|
|
65
|
+
- **Diagnostic guardrail:** `DesignTimeBuild`, `ResolveAssemblyReferencesSilent`,
|
|
66
|
+
and `ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch` affect build or
|
|
67
|
+
diagnostic behavior; changing them is not an established RAR performance fix.
|
|
68
|
+
- **Key insight:** RAR can legitimately execute on no-change builds to account
|
|
69
|
+
for external resolution state, including installed targeting packs or GAC
|
|
70
|
+
assemblies. Do not force it to skip using an incomplete freshness check.
|
|
71
|
+
- **Reduce transitive references carefully:** `DisableTransitiveProjectReferences`
|
|
72
|
+
is a last resort requiring explicit references to consumed dependencies.
|
|
73
|
+
`ReferenceOutputAssembly="false"` is appropriate when only build ordering is
|
|
74
|
+
needed, but still retains that ordering. Trim only unused package references.
|
|
75
|
+
Use the [dependency-graph audit](build-perf-baseline.md#step-4-dependency-graph-trimming).
|
|
76
|
+
|
|
77
|
+
### 2. Roslyn Analyzers and Source Generators
|
|
78
|
+
|
|
79
|
+
- **Symptoms:** `Csc` takes much longer than a comparable compilation, sometimes
|
|
80
|
+
more than twice a measured compiler-only scenario.
|
|
81
|
+
- **Diagnosis:** inspect `Csc` and analyzer/generator timing in the replayed log.
|
|
82
|
+
If timing detail is absent, obtain compiler-supported diagnostic timing rather
|
|
83
|
+
than guessing from package count. A controlled `-p:RunAnalyzers=false`
|
|
84
|
+
comparison must compile the same inputs on both sides, not compare a no-op
|
|
85
|
+
against forced compilation.
|
|
86
|
+
- **Conditional development policy:** after measuring and agreeing the policy,
|
|
87
|
+
examples include:
|
|
88
|
+
|
|
89
|
+
```xml
|
|
90
|
+
<PropertyGroup>
|
|
91
|
+
<RunAnalyzers Condition="'$(ContinuousIntegrationBuild)' != 'true'">false</RunAnalyzers>
|
|
92
|
+
<EnforceCodeStyleInBuild Condition="'$(ContinuousIntegrationBuild)' == 'true'">true</EnforceCodeStyleInBuild>
|
|
93
|
+
</PropertyGroup>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- **Per-configuration:** if development Debug builds are the intended scope, also
|
|
97
|
+
guard the CI case, for example
|
|
98
|
+
`Condition="'$(Configuration)' == 'Debug' and '$(ContinuousIntegrationBuild)' != 'true'"`.
|
|
99
|
+
CI can build Debug too.
|
|
100
|
+
- **Rule/package scope:** configure genuinely redundant or expensive diagnostics
|
|
101
|
+
appropriately, including `.editorconfig` severity policy, instead of blindly
|
|
102
|
+
removing analyzer packages.
|
|
103
|
+
- **Key principle:** preserve required analyzer enforcement in CI. Verify effective
|
|
104
|
+
properties and that the pipeline actually sets the CI signal. A conditional
|
|
105
|
+
assignment does not undo an earlier or later unconditional override.
|
|
106
|
+
- **Source generators:** do not assume `RunAnalyzers=false` disables or measures
|
|
107
|
+
every generator. Preserve required generated code and inspect generator-specific
|
|
108
|
+
evidence.
|
|
109
|
+
- **GlobalPackageReference:** examine the project, `Directory.Build.props`,
|
|
110
|
+
`Directory.Packages.props`, and package imports. Active global analyzer
|
|
111
|
+
references can affect production and test projects; a declaration alone does
|
|
112
|
+
not prove restoration or inclusion in the compiler's analyzer list.
|
|
113
|
+
- **EnforceCodeStyleInBuild:** when enabled it adds code-style checks to compiler
|
|
114
|
+
execution; it does not mean analyzers execute when compilation itself skips.
|
|
115
|
+
Conditionalize only under an agreed development/CI policy.
|
|
116
|
+
|
|
117
|
+
### 3. Serialization Bottlenecks (Single-threaded Targets)
|
|
118
|
+
|
|
119
|
+
- **Symptoms:** one project dominates while other nodes are idle or waiting.
|
|
120
|
+
- **Common culprits:** a long dependency chain, a critical-path project, or custom
|
|
121
|
+
orchestration issuing independent project requests sequentially.
|
|
122
|
+
- **Fixes:** optimize the measured critical path, remove false dependencies, or
|
|
123
|
+
split a bottleneck only when it exposes independent work. In custom `MSBuild`
|
|
124
|
+
tasks, `BuildInParallel="true"` also needs multiple available nodes.
|
|
125
|
+
- Follow [parallel build tuning](build-perf-baseline.md#step-6-parallel-build-tuning).
|
|
126
|
+
More nodes cannot parallelize required serial dependencies.
|
|
127
|
+
|
|
128
|
+
### 4. Excessive File I/O (Copy Tasks)
|
|
129
|
+
|
|
130
|
+
- **Symptoms:** high Copy aggregate time or excessive copy invocations.
|
|
131
|
+
- **Root causes:** thousands of unnecessary files, network destinations, or
|
|
132
|
+
accidental per-item task batching instead of an appropriate item batch.
|
|
133
|
+
- **First fixes:** inspect item metadata, source/destination paths, and invocation
|
|
134
|
+
counts. Reduce only unnecessary copies and choose the correct
|
|
135
|
+
[copy mode](copy-to-output-directory.md); replacing `Always` blindly with
|
|
136
|
+
`PreserveNewest` can leave mutated destination files stale.
|
|
137
|
+
- **Skip unchanged files:** inspect whether the actual target consumes
|
|
138
|
+
`SkipCopyUnchangedFiles`; it may already be true. Custom `Copy` tasks expose
|
|
139
|
+
`SkipUnchangedFiles`. Neither setting is a content-hash comparison.
|
|
140
|
+
- **Hardlinks:** identify the hot copy target in the binlog, then set only the
|
|
141
|
+
property that target passes to `Copy.UseHardlinksIfPossible`:
|
|
142
|
+
`CreateHardLinksForCopyFilesToOutputDirectoryIfPossible` for primary build
|
|
143
|
+
outputs, `CreateHardLinksForAdditionalFilesIfPossible` for content/additional
|
|
144
|
+
files, or `CreateHardLinksForCopyLocalIfPossible` for copy-local references.
|
|
145
|
+
Custom `Copy` tasks need their own `UseHardlinksIfPossible` value. Hardlinks
|
|
146
|
+
can help compatible same-volume, immutable-file workloads. A destination
|
|
147
|
+
mutation through a hardlink can modify its source, so do not use this for
|
|
148
|
+
writable/resettable content.
|
|
149
|
+
- **Shared layout:** `UseCommonOutputDirectory=true` assumes a genuinely shared,
|
|
150
|
+
collision-free output layout. Verify ownership and consumers first.
|
|
151
|
+
[Artifacts output](build-perf-baseline.md#step-2-artifacts-output-layout), including
|
|
152
|
+
`--artifacts-path`, requires SDK 8+ and is a separate measured layout change.
|
|
153
|
+
- **Dev Drive:** on supported Windows development or self-hosted CI machines,
|
|
154
|
+
evaluate Dev Drive for a measured I/O bottleneck. Do not promise automatic
|
|
155
|
+
copy-on-write gains for every task or disable security tooling for a benchmark.
|
|
156
|
+
|
|
157
|
+
### 5. Evaluation Overhead
|
|
158
|
+
|
|
159
|
+
- **Symptoms:** significant startup/evaluation time before target execution.
|
|
160
|
+
- **Root causes:** expensive `Directory.Build.props` logic, globs traversing
|
|
161
|
+
large trees, imports, or SDK-resolution overhead. NuGet-based SDK resolver
|
|
162
|
+
latency depends on the toolset and environment; historical timings are not a
|
|
163
|
+
fixed per-project cost.
|
|
164
|
+
- **Fixes:** simplify only measured costly logic and narrow offending globs.
|
|
165
|
+
`EnableDefaultItems=false` is a last resort requiring complete intentional item
|
|
166
|
+
lists, not the first optimization. Changing SDK resolution must preserve the
|
|
167
|
+
build's actual SDK contract.
|
|
168
|
+
- Use [evaluation performance](eval-performance.md) for focused evidence and fixes.
|
|
169
|
+
|
|
170
|
+
### 6. NuGet Restore in Build
|
|
171
|
+
|
|
172
|
+
- **Symptoms:** restore contributes substantial work repeatedly. Distinguish an
|
|
173
|
+
up-to-date check from actual repeated network/dependency-resolution work.
|
|
174
|
+
- **Separate restore from build:** successfully restore matching inputs, then
|
|
175
|
+
use `dotnet build --no-restore`. Keep build-only and restore-inclusive baselines
|
|
176
|
+
distinct; dependency changes require fresh assets.
|
|
177
|
+
- **Static graph restore:** `RestoreUseStaticGraphEvaluation=true` in
|
|
178
|
+
`Directory.Build.props` is a candidate for a measured large restore graph.
|
|
179
|
+
Validate equivalent resolved assets and toolset support. It is distinct from
|
|
180
|
+
graph build and its benefit is workload-dependent.
|
|
181
|
+
- Investigate changing inputs and feed/cache behavior rather than purging shared
|
|
182
|
+
package caches.
|
|
183
|
+
|
|
184
|
+
### 7. Large Project Count and Graph Shape
|
|
185
|
+
|
|
186
|
+
- **Symptoms:** many tiny projects add evaluation/invocation overhead, or deep
|
|
187
|
+
dependency chains serialize otherwise parallelizable work.
|
|
188
|
+
- **Consider:** graph mode for scheduling, consolidating small leaves for measured
|
|
189
|
+
overhead, or splitting a large bottleneck into independent pieces. These are
|
|
190
|
+
different remedies for different evidence.
|
|
191
|
+
- **Graph shape matters:** wide independent branches expose parallelism; a long
|
|
192
|
+
required chain remains serial regardless of node count.
|
|
193
|
+
- **Actions:** identify the exact unnecessary edge or costly project. Preserve
|
|
194
|
+
direct API/metadata contracts and compare the resulting critical path.
|
|
195
|
+
[Static graph builds](build-perf-baseline.md#step-5-static-graph-builds-graph)
|
|
196
|
+
do not automatically provide isolation or a cache.
|
|
197
|
+
|
|
198
|
+
## Using Binlog Replay for Performance Analysis
|
|
199
|
+
|
|
200
|
+
1. [Replay the existing binlog](binlog-failure-analysis.md#replay-a-binary-log)
|
|
201
|
+
with diagnostic verbosity and a performance summary.
|
|
202
|
+
2. Read the target/task summaries, usually near the end of the replayed log:
|
|
203
|
+
|
|
204
|
+
```powershell
|
|
205
|
+
Select-String -Path .\full-01.log -Pattern 'Target Performance Summary|Task Performance Summary' -Context 0,50
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Use the cumulative timings to select candidates, not to compute additive
|
|
209
|
+
percentages of elapsed build time.
|
|
210
|
+
3. Find per-project timings and instances:
|
|
211
|
+
|
|
212
|
+
```powershell
|
|
213
|
+
Select-String -Path .\full-01.log -Pattern 'Done Building Project|Project Performance Summary' -Context 0,20
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
4. Inspect recorded scheduling evidence:
|
|
217
|
+
|
|
218
|
+
```powershell
|
|
219
|
+
Select-String -Path .\full-01.log -Pattern 'node.*assigned|building with|scheduler'
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
These messages alone are not a utilization measurement. Report missing
|
|
223
|
+
scheduling/timestamp detail rather than inferring an exact timeline.
|
|
224
|
+
5. Check analyzer timing, when the original capture contains it:
|
|
225
|
+
|
|
226
|
+
```powershell
|
|
227
|
+
Select-String -Path .\full-01.log -Pattern 'Total analyzer execution time|analyzer.*elapsed|CompilerAnalyzerDriver'
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
6. Drill into the relevant project's slow target and its tasks:
|
|
231
|
+
|
|
232
|
+
```powershell
|
|
233
|
+
Select-String -Path .\full-01.log -Pattern 'Target "CoreCompile"|Target "ResolveAssemblyReferences"' -Context 0,20
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Quick Wins Checklist
|
|
237
|
+
|
|
238
|
+
Choose only items supported by the diagnosis, one experiment at a time:
|
|
239
|
+
|
|
240
|
+
- [ ] Confirm the intended `-m` node budget rather than assuming host defaults.
|
|
241
|
+
- [ ] Separate restore and build only with current matching assets.
|
|
242
|
+
- [ ] Measure static graph restore and verify equivalent assets.
|
|
243
|
+
- [ ] Consider hardlinks only for compatible immutable-file workloads.
|
|
244
|
+
- [ ] Scope analyzer changes to an agreed development policy; retain CI enforcement.
|
|
245
|
+
- [ ] Inspect `ProduceReferenceAssembly` before enabling it; many SDK configurations
|
|
246
|
+
already enable it. Verify implementation-only and public-API changes downstream.
|
|
247
|
+
- [ ] Check [incremental behavior](incremental-build.md) on an ordinary second build.
|
|
248
|
+
- [ ] Inspect evaluated output/intermediate paths for collisions between projects,
|
|
249
|
+
configurations, TFMs, and RIDs; do not just give everything one common directory.
|
|
250
|
+
- [ ] Measure graph mode for a statically discoverable multi-project build.
|
|
251
|
+
- [ ] Evaluate SDK 8+ artifacts layout only with its path-consumer checks.
|
|
252
|
+
- [ ] Consider a separate Dev Drive environment experiment for Windows I/O limits.
|
|
253
|
+
|
|
254
|
+
## Impact Categorization
|
|
255
|
+
|
|
256
|
+
- **HIGH IMPACT:** a verified critical-path cost above roughly 10% of elapsed
|
|
257
|
+
build time, or substantial real work behind a target reported above 50%.
|
|
258
|
+
- **MEDIUM IMPACT:** verified costs around 2-10%.
|
|
259
|
+
- **QUICK WINS:** low-effort changes with demonstrated modest benefit, not
|
|
260
|
+
unmeasured property toggles.
|
|
261
|
+
|
|
262
|
+
Report the project/TFM, target/task, evidence, one targeted change, and a
|
|
263
|
+
same-scenario before/after comparison with spread. Verify required artifacts,
|
|
264
|
+
diagnostics, and relevant tests/consumers. Stop if the result is unmeasured, within
|
|
265
|
+
noise, or breaks correctness; do not claim success from disabled validation or
|
|
266
|
+
inclusive timing arithmetic.
|
|
267
|
+
|
|
268
|
+
> **Cratis:** Only as a labelled timing experiment; restore analyzers and warnings-as-errors and run the real gate before finishing.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Choosing a CopyToOutputDirectory Mode
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Overview
|
|
6
|
+
- Why `Always` Is Usually the Wrong Choice
|
|
7
|
+
- `IfDifferent`: Copy When Different, in Either Direction
|
|
8
|
+
- Globally Softening `Always` with `$(SkipUnchangedFilesOnCopyAlways)`
|
|
9
|
+
- How the Modes Flow Through the Build
|
|
10
|
+
- Version Requirement
|
|
11
|
+
- Quick Decision Guide
|
|
12
|
+
- Verify the Choice
|
|
13
|
+
|
|
14
|
+
Source: original copy to output directory skill.
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
`CopyToOutputDirectory` metadata controls whether an item such as `Content`,
|
|
19
|
+
`None`, `EmbeddedResource`, or `Compile` is copied next to the build output.
|
|
20
|
+
`CopyToPublishDirectory` controls the corresponding publish selection. Picking
|
|
21
|
+
the wrong mode can leave stale files or add recurring copy work.
|
|
22
|
+
|
|
23
|
+
As of **MSBuild 17.13 / .NET SDK 9.0.2xx**, the common build targets support four
|
|
24
|
+
values. Check the publishing SDK separately for publish behavior.
|
|
25
|
+
|
|
26
|
+
| Mode | Copies when | Incremental cost | Typical use |
|
|
27
|
+
| --- | --- | --- | --- |
|
|
28
|
+
| `Never` | Never through this copy pipeline | None | Files not needed in that output |
|
|
29
|
+
| `PreserveNewest` | Source is newer than destination, or destination is missing | Timestamp checks | Normal source-edited content |
|
|
30
|
+
| `Always` | Every build that reaches the copy target, unless a skip-unchanged override is enabled | Repeated copies in classic mode | A real unconditional-copy/reset requirement |
|
|
31
|
+
| `IfDifferent` | Timestamp or size differs in either direction, or destination is missing | Timestamp and size checks | Destination may be mutated between builds |
|
|
32
|
+
|
|
33
|
+
Omitted metadata commonly means `Never`, but SDKs/imports can supply item-specific
|
|
34
|
+
defaults. Inspect effective metadata before assuming omission disables copying.
|
|
35
|
+
|
|
36
|
+
The following `Include` examples are for explicitly included files. When an SDK
|
|
37
|
+
already includes a file, use `Update` on its actual item type instead; for example,
|
|
38
|
+
a web SDK can classify `appsettings.json` as `Content`, not `None`.
|
|
39
|
+
|
|
40
|
+
```xml
|
|
41
|
+
<ItemGroup>
|
|
42
|
+
<None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
|
|
43
|
+
<None Include="testdata\seed.db" CopyToOutputDirectory="IfDifferent" />
|
|
44
|
+
</ItemGroup>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The child-element form is equivalent:
|
|
48
|
+
|
|
49
|
+
```xml
|
|
50
|
+
<None Include="testdata\seed.db">
|
|
51
|
+
<CopyToOutputDirectory>IfDifferent</CopyToOutputDirectory>
|
|
52
|
+
</None>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Why `Always` Is Usually the Wrong Choice
|
|
56
|
+
|
|
57
|
+
Classic `Always` re-copies files whenever that copy pipeline executes, including
|
|
58
|
+
otherwise no-change MSBuild builds. Many or large content files can make this a
|
|
59
|
+
measurable recurring cost. An IDE up-to-date check that skips MSBuild entirely is
|
|
60
|
+
a separate decision.
|
|
61
|
+
|
|
62
|
+
Historically, `Always` addressed a specific scenario: **the destination changes
|
|
63
|
+
between builds**. Tests or applications might mutate a database, state file, or
|
|
64
|
+
configuration file. With `PreserveNewest`, a newer destination is not reset
|
|
65
|
+
because the source is no longer newer. `Always` forces a reset, but pays for it
|
|
66
|
+
even when the destination has not changed.
|
|
67
|
+
|
|
68
|
+
Do not replace `Always` solely because it appears in project text. First measure
|
|
69
|
+
copy work and establish whether unconditional copying is part of the required
|
|
70
|
+
behavior.
|
|
71
|
+
|
|
72
|
+
## `IfDifferent`: Copy When Different, in Either Direction
|
|
73
|
+
|
|
74
|
+
`IfDifferent` restores the source when the destination is missing or its timestamp
|
|
75
|
+
or size differs, whether the destination is newer or older. It skips unchanged
|
|
76
|
+
files according to that metadata comparison.
|
|
77
|
+
|
|
78
|
+
`_CopyDifferingSourceItemsToOutputDirectory` uses the `Copy` task with
|
|
79
|
+
`SkipUnchangedFiles="true"`. This is a **timestamp-and-size heuristic, not a content
|
|
80
|
+
hash**. A destination edited to the same size and last-write timestamp is treated
|
|
81
|
+
as unchanged and is not restored.
|
|
82
|
+
|
|
83
|
+
Use `IfDifferent` when:
|
|
84
|
+
|
|
85
|
+
- Tests or the application mutate copied databases, state/storage files, or
|
|
86
|
+
editable configuration, and the metadata-based reset meets the contract.
|
|
87
|
+
- `Always` was used only to synchronize a drifting destination, not because
|
|
88
|
+
every build must perform a fresh copy.
|
|
89
|
+
|
|
90
|
+
```xml
|
|
91
|
+
<ItemGroup>
|
|
92
|
+
<!-- For an explicitly included fixture; use Update if already included -->
|
|
93
|
+
<None Include="fixtures\catalog.db" CopyToOutputDirectory="IfDifferent" />
|
|
94
|
+
</ItemGroup>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
If same-size/same-timestamp mutations must also be reset, retain genuine
|
|
98
|
+
unconditional copying or a separately verified content-based mechanism.
|
|
99
|
+
|
|
100
|
+
## Globally Softening `Always` with `$(SkipUnchangedFilesOnCopyAlways)`
|
|
101
|
+
|
|
102
|
+
For a codebase with many legacy `Always` items, a bulk opt-in is:
|
|
103
|
+
|
|
104
|
+
```xml
|
|
105
|
+
<PropertyGroup>
|
|
106
|
+
<SkipUnchangedFilesOnCopyAlways>true</SkipUnchangedFilesOnCopyAlways>
|
|
107
|
+
</PropertyGroup>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The common `Always` copy target then passes `SkipUnchangedFiles="true"` to `Copy`,
|
|
111
|
+
giving those items the same timestamp/size skip heuristic as `IfDifferent`.
|
|
112
|
+
|
|
113
|
+
- Unset/default behavior is effectively `false`: classic `Always` copying.
|
|
114
|
+
- Setting it in `Directory.Build.props` can affect the entire repository.
|
|
115
|
+
- Prefer per-item intent when practical. A bulk property avoids individual item
|
|
116
|
+
edits, but still changes behavior and requires checking every affected reset
|
|
117
|
+
contract.
|
|
118
|
+
- Do not assume this build-target property governs every publish copy task.
|
|
119
|
+
|
|
120
|
+
## How the Modes Flow Through the Build
|
|
121
|
+
|
|
122
|
+
`GetCopyToOutputDirectoryItems` buckets items by their metadata. The common
|
|
123
|
+
`_CopySourceItemsToOutputDirectory` pipeline, invoked by
|
|
124
|
+
`CopyFilesToOutputDirectory`, uses:
|
|
125
|
+
|
|
126
|
+
- `_CopyOutOfDateSourceItemsToOutputDirectory` for `PreserveNewest`, with
|
|
127
|
+
`Inputs`/`Outputs` timestamp checks.
|
|
128
|
+
- `_CopyOutOfDateSourceItemsToOutputDirectoryAlways` for `Always`, with the
|
|
129
|
+
skip-unchanged property controlling its `Copy` task.
|
|
130
|
+
- `_CopyDifferingSourceItemsToOutputDirectory` for `IfDifferent`, with
|
|
131
|
+
`SkipUnchangedFiles="true"`.
|
|
132
|
+
|
|
133
|
+
These standard build copies register `FileWrites` for the normal Clean pipeline.
|
|
134
|
+
Custom tasks and publish outputs can have different tracking; inspect ownership
|
|
135
|
+
and use only approved, scoped cleanup.
|
|
136
|
+
|
|
137
|
+
**Transitive copy:** copyable items can flow to referencing projects through
|
|
138
|
+
`ProjectReference`, including `_CopyToOutputDirectoryTransitiveItems`; `Never`
|
|
139
|
+
does not participate in that copy pipeline. Verify consumers as well as the
|
|
140
|
+
defining project. Supporting targets also collect `IfDifferent` for ClickOnce;
|
|
141
|
+
test the publishing mode and supported toolset actually used by the repository.
|
|
142
|
+
|
|
143
|
+
## Version Requirement
|
|
144
|
+
|
|
145
|
+
`IfDifferent` and `SkipUnchangedFilesOnCopyAlways` require **MSBuild 17.13+**
|
|
146
|
+
(**.NET SDK 9.0.2xx+ / Visual Studio 2022 17.13+**) for the common build-target
|
|
147
|
+
behavior described here. Older common targets can silently omit an item whose
|
|
148
|
+
mode they do not recognize.
|
|
149
|
+
|
|
150
|
+
Check the actual SDK/MSBuild host and the minimum supported toolset, not the
|
|
151
|
+
target framework. Gate usage or require a compatible SDK through `global.json`,
|
|
152
|
+
and check Visual Studio/MSBuild.exe hosts too. On older hosts, retaining `Always`
|
|
153
|
+
can preserve required reset behavior; substituting `PreserveNewest` may not.
|
|
154
|
+
An unsupported skip-unchanged property is not evidence of a speedup.
|
|
155
|
+
|
|
156
|
+
`CopyToPublishDirectory` is implemented by publishing targets. Verify recognition,
|
|
157
|
+
transitive collection, and reset behavior on the selected publishing SDK rather
|
|
158
|
+
than inferring support from the common build-target version alone.
|
|
159
|
+
|
|
160
|
+
## Quick Decision Guide
|
|
161
|
+
|
|
162
|
+
- Not needed in this output: explicitly choose `Never` when defaults might copy it.
|
|
163
|
+
- Normal source-edited file: `PreserveNewest`.
|
|
164
|
+
- Mutated destination should reset when metadata differs: `IfDifferent`.
|
|
165
|
+
- Fresh copy required on every pipeline execution, including metadata-identical
|
|
166
|
+
changes: `Always`, without a skip-unchanged override.
|
|
167
|
+
- Many compatible legacy `Always` items: consider
|
|
168
|
+
`SkipUnchangedFilesOnCopyAlways=true` after checking the same behavioral limits.
|
|
169
|
+
|
|
170
|
+
## Verify the Choice
|
|
171
|
+
|
|
172
|
+
In an approved disposable workspace or on explicitly scoped files, check an
|
|
173
|
+
ordinary no-change build, a source edit, a missing destination, and destination
|
|
174
|
+
mutations with newer and older timestamps. Check same-size/same-timestamp mutation
|
|
175
|
+
when strict reset matters. Also test source addition/removal and the intended
|
|
176
|
+
stale-output policy: copy metadata alone does not guarantee obsolete destinations
|
|
177
|
+
are deleted.
|
|
178
|
+
|
|
179
|
+
Check build, referencing projects, runtime use, and relevant publish/ClickOnce
|
|
180
|
+
outputs. Do not hardlink writable/resettable outputs to sources: writes through a
|
|
181
|
+
hardlink can change the source. Compare copy counts and same-scenario timings
|
|
182
|
+
using the [baseline](build-perf-baseline.md); preserve the output contract rather
|
|
183
|
+
than claiming success merely because fewer files were copied.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Diagnosing MSBuild Evaluation Performance
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Confirm the Problem Before Changing Anything
|
|
6
|
+
- MSBuild Evaluation Phases
|
|
7
|
+
- Diagnosing Evaluation Performance
|
|
8
|
+
- Expensive Glob Patterns
|
|
9
|
+
- Import Chain Analysis
|
|
10
|
+
- Multiple Evaluations
|
|
11
|
+
- TreatAsLocalProperty
|
|
12
|
+
- Property Function Cost
|
|
13
|
+
- Optimization Checklist
|
|
14
|
+
|
|
15
|
+
Source: original eval performance skill.
|
|
16
|
+
|
|
17
|
+
Evaluation is work MSBuild does before executing targets: reading project files,
|
|
18
|
+
processing imports, expanding globs, and establishing definitions. Find and
|
|
19
|
+
confirm the bottleneck first; recommend a change only when measurements justify it.
|
|
20
|
+
|
|
21
|
+
## Confirm the Problem Before Changing Anything
|
|
22
|
+
|
|
23
|
+
Engage only when evaluation is **measurably** costly. Do not act when:
|
|
24
|
+
|
|
25
|
+
- **Slowness is in compilation or target execution.** Use
|
|
26
|
+
[build performance diagnostics](build-perf-diagnostics.md).
|
|
27
|
+
- **The complaint is unnecessary rebuilding.** Use
|
|
28
|
+
[incremental-build diagnosis](incremental-build.md).
|
|
29
|
+
- **There is no measurement.** Reuse a matching binlog or timing summary, or
|
|
30
|
+
collect one through [binlog generation](binlog-generation.md). Do not infer
|
|
31
|
+
slowness solely from project syntax.
|
|
32
|
+
- **A pattern below appears but evaluation is already fast.** Broad globs, deep
|
|
33
|
+
imports, and item-default settings are not reasons to rewrite healthy builds.
|
|
34
|
+
|
|
35
|
+
Report unmeasured patterns as observations, not causes. Prefer the smallest
|
|
36
|
+
targeted change; never disable SDK defaults as the first move.
|
|
37
|
+
|
|
38
|
+
## MSBuild Evaluation Phases
|
|
39
|
+
|
|
40
|
+
1. **Initial properties:** environment variables, global properties, and reserved properties.
|
|
41
|
+
2. **Imports and properties:** process imports and property groups in evaluation order.
|
|
42
|
+
3. **Item definitions:** establish metadata defaults.
|
|
43
|
+
4. **Items:** process `Include`, `Remove`, `Update`, and glob expansion.
|
|
44
|
+
5. **UsingTask definitions:** register task implementations.
|
|
45
|
+
6. **Target definitions:** establish targets for subsequent execution.
|
|
46
|
+
|
|
47
|
+
Evaluation precedes execution for a project instance. It can be expensive even
|
|
48
|
+
when compilation later skips; a source generator running in `Csc` is execution,
|
|
49
|
+
not project evaluation.
|
|
50
|
+
|
|
51
|
+
## Diagnosing Evaluation Performance
|
|
52
|
+
|
|
53
|
+
### Using Binlog Replay
|
|
54
|
+
|
|
55
|
+
1. [Replay the existing binary log](binlog-failure-analysis.md#replay-a-binary-log)
|
|
56
|
+
locally with diagnostic verbosity and performance summaries. The search below uses that
|
|
57
|
+
command's `full-01.log`; substitute the actual path if you chose another name.
|
|
58
|
+
2. Look for evaluation timing and instance information:
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
Select-String -Path .\full-01.log -Pattern 'Project Evaluation Performance Summary|Evaluation started|Evaluation finished|Project evaluation' -Context 0,20
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
3. Record the expensive project instance, global properties, configuration,
|
|
65
|
+
TFM/RID, and invocation purpose. Distinct instances may be expected rather
|
|
66
|
+
than duplicate work.
|
|
67
|
+
4. Inspect its imports and property/item definitions. If the capture lacks
|
|
68
|
+
detailed evaluation timing, do not fabricate it from the number of imports.
|
|
69
|
+
On supporting MSBuild versions, `-profileEvaluation:eval.tsv` provides a
|
|
70
|
+
focused profile; record the extra instrumentation separately.
|
|
71
|
+
|
|
72
|
+
### Using /pp (Preprocess)
|
|
73
|
+
|
|
74
|
+
```powershell
|
|
75
|
+
dotnet msbuild .\MyProject.csproj "-pp:full.xml"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- Supply the same configuration/TFM/global properties as the affected instance.
|
|
79
|
+
- `/pp` inlines imports and shows their boundaries and definitions.
|
|
80
|
+
- It does **not** fully expand every property/item value or measure evaluation cost.
|
|
81
|
+
- Over 10,000 lines can be a clue to inspect, not proof of heavy evaluation.
|
|
82
|
+
|
|
83
|
+
For evaluated values, **MSBuild 17.8+** also supports queries such as:
|
|
84
|
+
|
|
85
|
+
```powershell
|
|
86
|
+
dotnet msbuild .\MyProject.csproj -p:TargetFramework=net8.0 -getProperty:Configuration,TargetFramework -getItem:Compile
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Without a target request, this evaluates rather than builds. On older hosts, use
|
|
90
|
+
the available diagnostic evidence instead of assuming these query switches exist.
|
|
91
|
+
|
|
92
|
+
### Using /clp:PerformanceSummary
|
|
93
|
+
|
|
94
|
+
Add `-clp:PerformanceSummary` to the same build invocation for evaluation,
|
|
95
|
+
project, target, and task summaries. Keep evaluation separate from execution,
|
|
96
|
+
and remember that cumulative/nested/parallel durations are not additive wall time.
|
|
97
|
+
|
|
98
|
+
## Expensive Glob Patterns
|
|
99
|
+
|
|
100
|
+
Pursue these remedies only when item evaluation is slow and the globs are the
|
|
101
|
+
measured cause; a custom glob over a small intended tree is fine.
|
|
102
|
+
|
|
103
|
+
- Broad patterns such as `**\*.cs` can traverse large trees.
|
|
104
|
+
- SDK default globs have exclusions; custom globs may not share them.
|
|
105
|
+
- Watch for unexpected traversal of `node_modules`, `.git`, and generated
|
|
106
|
+
`bin`/`obj` trees, not just the text of the pattern.
|
|
107
|
+
- Append appropriate `DefaultItemExcludes` rather than replacing existing
|
|
108
|
+
exclusions. A custom item must actually consume the exclusion property.
|
|
109
|
+
- Narrow roots/file types where equivalent, such as `src\**\*.cs`.
|
|
110
|
+
- `EnableDefaultItems=false` is a last resort requiring an intentional
|
|
111
|
+
replacement for compile/resource/content discovery and future file changes.
|
|
112
|
+
- Inspect evaluated `Compile` and other affected items for unexpected files.
|
|
113
|
+
|
|
114
|
+
For an offending custom item, adapt exclusions and paths to the intended input set:
|
|
115
|
+
|
|
116
|
+
```xml
|
|
117
|
+
<PropertyGroup>
|
|
118
|
+
<DefaultItemExcludes>$(DefaultItemExcludes);**\node_modules\**;**\.git\**</DefaultItemExcludes>
|
|
119
|
+
</PropertyGroup>
|
|
120
|
+
<ItemGroup>
|
|
121
|
+
<AdditionalFiles Include="src\**\*.json" Exclude="$(DefaultItemExcludes)" />
|
|
122
|
+
</ItemGroup>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Verify item equivalence and file addition/removal behavior, not just a faster scan.
|
|
126
|
+
|
|
127
|
+
## Import Chain Analysis
|
|
128
|
+
|
|
129
|
+
- Deep chains, for example over 20 levels, are candidates to inspect, not
|
|
130
|
+
inherently slow builds.
|
|
131
|
+
- Imports require reading, parsing, and evaluation; measure the costly portion.
|
|
132
|
+
- Common sources include package `.props`/`.targets`, framework SDK imports,
|
|
133
|
+
and `Directory.Build` chains.
|
|
134
|
+
- Use `/pp` import-boundary comments and file paths to understand ownership and
|
|
135
|
+
nesting; do not depend on one exact comment phrase.
|
|
136
|
+
- Consolidate/remove imports or package-provided build content only when the
|
|
137
|
+
chain is measurably costly and all property/item/target consumers are preserved.
|
|
138
|
+
|
|
139
|
+
## Multiple Evaluations
|
|
140
|
+
|
|
141
|
+
- The same project path can legitimately have multiple evaluations.
|
|
142
|
+
- Different global-property sets, TFM/RID/configurations, outer/inner
|
|
143
|
+
multi-targeting builds, restore, and design-time builds can require distinct
|
|
144
|
+
instances.
|
|
145
|
+
- Compare instance identity and call purpose, not just an evaluation count or a
|
|
146
|
+
rule of "one evaluation per project per TFM."
|
|
147
|
+
- Investigate callers that introduce accidental global-property differences.
|
|
148
|
+
Normalize only differences that do not change intended behavior.
|
|
149
|
+
- [Graph build](build-perf-baseline.md#step-5-static-graph-builds-graph) is an
|
|
150
|
+
experiment for compatible graphs, not a guarantee of evaluation deduplication.
|
|
151
|
+
|
|
152
|
+
## TreatAsLocalProperty
|
|
153
|
+
|
|
154
|
+
- Allows listed global properties to be overridden within the current project.
|
|
155
|
+
- A local override is not automatically forwarded as a new global value to
|
|
156
|
+
child projects. For example, a command-line value can still reach a child even
|
|
157
|
+
though the parent locally changed that property.
|
|
158
|
+
- It is therefore not a blanket mechanism for removing inherited properties
|
|
159
|
+
from child builds. Use explicit `MSBuild` task property controls, such as
|
|
160
|
+
`Properties` or `RemoveProperties`, when that is the intended contract.
|
|
161
|
+
- Use it when local override semantics are genuinely needed. The number of names
|
|
162
|
+
listed is not by itself evidence of measurable evaluation overhead.
|
|
163
|
+
|
|
164
|
+
## Property Function Cost
|
|
165
|
+
|
|
166
|
+
- Property functions used during evaluation run at evaluation time; expressions
|
|
167
|
+
inside targets instead run when that execution reaches them.
|
|
168
|
+
- Most string operations are cheap.
|
|
169
|
+
- `$([System.IO.File]::ReadAllText(...))` can repeat file I/O on each evaluation.
|
|
170
|
+
- Network access and heavy computations can be costly; measure the actual
|
|
171
|
+
function rather than declaring every file read a bottleneck.
|
|
172
|
+
- Keep evaluation-time functions fast and side-effect-free. Move work into a
|
|
173
|
+
correctly incremental target only if evaluation-time consumers do not need
|
|
174
|
+
its result.
|
|
175
|
+
|
|
176
|
+
## Optimization Checklist
|
|
177
|
+
|
|
178
|
+
- [ ] Inspect `/pp` definitions/imports without treating line count as timing.
|
|
179
|
+
- [ ] Explain evaluation counts by project-instance identity.
|
|
180
|
+
- [ ] Exclude large unintended directories from measured costly globs.
|
|
181
|
+
- [ ] Reduce measured unnecessary evaluation-time I/O.
|
|
182
|
+
- [ ] Simplify expensive import chains while preserving consumers.
|
|
183
|
+
- [ ] Compare graph mode only where statically discoverable and relevant.
|
|
184
|
+
- [ ] Remove `UsingTask` declarations only if their consumers and measurable cost
|
|
185
|
+
justify it.
|
|
186
|
+
|
|
187
|
+
Repeat the same build scenario and evaluate the same intended item/property sets.
|
|
188
|
+
Check additions, changes, removals, affected configurations, and downstream
|
|
189
|
+
behavior. Report timing and evidence before/after; leave configuration unchanged
|
|
190
|
+
when evaluation is cheap, unmeasured, or the apparent gain is within noise.
|