@nklisch/pi-agile-workflow 0.15.3
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/.claude-plugin/plugin.json +10 -0
- package/.codex-plugin/plugin.json +28 -0
- package/CHANGELOG.md +591 -0
- package/README.md +107 -0
- package/docs/ARCHITECTURE.md +716 -0
- package/docs/MIGRATION.md +419 -0
- package/docs/PRINCIPLES.md +464 -0
- package/docs/ROADMAP.md +95 -0
- package/docs/SPEC.md +855 -0
- package/docs/VISION.md +106 -0
- package/extensions/agile-workflow.test.ts +419 -0
- package/extensions/agile-workflow.ts +467 -0
- package/hooks/hooks.json +52 -0
- package/hooks/scripts/prompt-context.py +628 -0
- package/hooks/scripts/substrate-maintainer.py +359 -0
- package/hooks/scripts/test_prompt_context.py +757 -0
- package/package.json +30 -0
- package/scripts/install-work-view.sh +165 -0
- package/scripts/tests/agent-metadata.test.sh +75 -0
- package/scripts/tests/bump-version.test.sh +552 -0
- package/scripts/tests/channel-parity.test.sh +117 -0
- package/scripts/tests/convert-content-integrity.test.sh +246 -0
- package/scripts/tests/convert-install-routing.test.sh +159 -0
- package/scripts/tests/install-work-view.test.sh +411 -0
- package/scripts/tests/pi-package-metadata.test.sh +119 -0
- package/scripts/tests/work-board-shim.test.sh +239 -0
- package/scripts/tests/work-view-dist-version.test.sh +105 -0
- package/scripts/work-board.sh +64 -0
- package/scripts/work-view.sh +423 -0
- package/skills/autopilot/SKILL.md +330 -0
- package/skills/autopilot/agents/openai.yaml +6 -0
- package/skills/board/SKILL.md +62 -0
- package/skills/board/agents/openai.yaml +6 -0
- package/skills/bold-refactor/SKILL.md +244 -0
- package/skills/bold-refactor/agents/openai.yaml +6 -0
- package/skills/bug-scan/SKILL.md +378 -0
- package/skills/bug-scan/agents/openai.yaml +6 -0
- package/skills/bug-scan/references/async-promises.md +119 -0
- package/skills/bug-scan/references/concurrency-races.md +148 -0
- package/skills/bug-scan/references/data-layer.md +167 -0
- package/skills/bug-scan/references/error-handling.md +197 -0
- package/skills/bug-scan/references/gate-item-template.md +94 -0
- package/skills/bug-scan/references/language-footguns.md +219 -0
- package/skills/bug-scan/references/parked-item-template.md +103 -0
- package/skills/bug-scan/references/report-template.md +115 -0
- package/skills/bug-scan/references/resource-leaks.md +199 -0
- package/skills/bug-scan/references/state-closures.md +148 -0
- package/skills/bug-scan/references/time-numbers.md +158 -0
- package/skills/convert/SKILL.md +1463 -0
- package/skills/convert/agents/openai.yaml +6 -0
- package/skills/convert/references/legacy-overlap-migration.md +179 -0
- package/skills/deep-code-scan/SKILL.md +378 -0
- package/skills/deep-code-scan/agents/openai.yaml +6 -0
- package/skills/deep-code-scan/references/artifact-ledger.md +148 -0
- package/skills/deep-code-scan/references/consolidation.md +91 -0
- package/skills/deep-code-scan/references/decomposition.md +127 -0
- package/skills/deep-code-scan/references/item-templates.md +263 -0
- package/skills/deep-code-scan/references/lane-catalog.md +129 -0
- package/skills/deep-code-scan/references/review-gauntlet.md +108 -0
- package/skills/deep-code-scan/references/scanner-brief.md +152 -0
- package/skills/e2e-test-design/SKILL.md +517 -0
- package/skills/e2e-test-design/references/anti-tautology.md +261 -0
- package/skills/e2e-test-design/references/service-mocks.md +234 -0
- package/skills/e2e-test-design/references/test-taxonomy.md +211 -0
- package/skills/epic-design/SKILL.md +476 -0
- package/skills/epicize/SKILL.md +210 -0
- package/skills/epicize/agents/openai.yaml +6 -0
- package/skills/feature-design/SKILL.md +472 -0
- package/skills/fix/SKILL.md +173 -0
- package/skills/gate-cruft/SKILL.md +258 -0
- package/skills/gate-docs/SKILL.md +278 -0
- package/skills/gate-patterns/SKILL.md +389 -0
- package/skills/gate-refactor/SKILL.md +408 -0
- package/skills/gate-security/SKILL.md +275 -0
- package/skills/gate-tests/SKILL.md +367 -0
- package/skills/groom/SKILL.md +162 -0
- package/skills/groom/agents/openai.yaml +6 -0
- package/skills/ideate/SKILL.md +185 -0
- package/skills/ideate/agents/openai.yaml +6 -0
- package/skills/implement/SKILL.md +288 -0
- package/skills/implement-orchestrator/SKILL.md +604 -0
- package/skills/park/SKILL.md +111 -0
- package/skills/perf-design/SKILL.md +427 -0
- package/skills/perf-scout/SKILL.md +404 -0
- package/skills/perf-scout/agents/openai.yaml +6 -0
- package/skills/perf-scout/references/algorithmic-and-data-structures.md +126 -0
- package/skills/perf-scout/references/approximation-and-precomputation.md +136 -0
- package/skills/perf-scout/references/caching-and-memoization.md +123 -0
- package/skills/perf-scout/references/compiler-runtime-and-language.md +136 -0
- package/skills/perf-scout/references/database-and-storage-internals.md +144 -0
- package/skills/perf-scout/references/distributed-systems.md +150 -0
- package/skills/perf-scout/references/game-engine-and-realtime.md +136 -0
- package/skills/perf-scout/references/gpu-and-accelerators.md +130 -0
- package/skills/perf-scout/references/idea-ranking.md +75 -0
- package/skills/perf-scout/references/io-and-batching.md +135 -0
- package/skills/perf-scout/references/memory-and-data-locality.md +137 -0
- package/skills/perf-scout/references/parallelism-and-vectorization.md +126 -0
- package/skills/perf-scout/references/parked-item-template.md +117 -0
- package/skills/perf-scout/references/peer-review-pass.md +150 -0
- package/skills/perf-scout/references/report-template.md +148 -0
- package/skills/principles/SKILL.md +678 -0
- package/skills/principles/references/models.md +191 -0
- package/skills/principles/references/subagents.md +158 -0
- package/skills/prose-author/SKILL.md +185 -0
- package/skills/refactor-conventions-creator/SKILL.md +359 -0
- package/skills/refactor-conventions-creator/agents/openai.yaml +6 -0
- package/skills/refactor-conventions-creator/references/common-structures.md +91 -0
- package/skills/refactor-conventions-creator/references/common-styles.md +90 -0
- package/skills/refactor-design/SKILL.md +384 -0
- package/skills/release-deploy/SKILL.md +667 -0
- package/skills/release-deploy/agents/openai.yaml +6 -0
- package/skills/research/SKILL.md +166 -0
- package/skills/research/agents/openai.yaml +6 -0
- package/skills/review/SKILL.md +267 -0
- package/skills/review/references/deep-review.md +98 -0
- package/skills/review/references/review-lenses.md +68 -0
- package/skills/review/references/substrate-side-effects.md +161 -0
- package/skills/review/references/target-resolution.md +52 -0
- package/skills/scope/SKILL.md +486 -0
- package/work-view/Cargo.lock +486 -0
- package/work-view/Cargo.toml +9 -0
- package/work-view/crates/cli/.work-view-version +1 -0
- package/work-view/crates/cli/Cargo.toml +18 -0
- package/work-view/crates/cli/src/actionable.rs +500 -0
- package/work-view/crates/cli/src/args.rs +792 -0
- package/work-view/crates/cli/src/board/assets/board.css +855 -0
- package/work-view/crates/cli/src/board/assets/board.js +259 -0
- package/work-view/crates/cli/src/board/assets/card.js +139 -0
- package/work-view/crates/cli/src/board/assets/components.css +290 -0
- package/work-view/crates/cli/src/board/assets/dependency.js +1193 -0
- package/work-view/crates/cli/src/board/assets/detail.js +256 -0
- package/work-view/crates/cli/src/board/assets/filters.js +389 -0
- package/work-view/crates/cli/src/board/assets/index.html +77 -0
- package/work-view/crates/cli/src/board/assets/kanban.js +231 -0
- package/work-view/crates/cli/src/board/assets/markdown.js +260 -0
- package/work-view/crates/cli/src/board/assets/motion.css +131 -0
- package/work-view/crates/cli/src/board/assets/state.js +245 -0
- package/work-view/crates/cli/src/board/assets/table.js +304 -0
- package/work-view/crates/cli/src/board/assets/tokens.css +602 -0
- package/work-view/crates/cli/src/board/assets/views.js +41 -0
- package/work-view/crates/cli/src/board/assets.rs +162 -0
- package/work-view/crates/cli/src/board/feed.rs +204 -0
- package/work-view/crates/cli/src/board/mod.rs +246 -0
- package/work-view/crates/cli/src/board/open.rs +144 -0
- package/work-view/crates/cli/src/board/server.rs +407 -0
- package/work-view/crates/cli/src/main.rs +195 -0
- package/work-view/crates/cli/src/render.rs +354 -0
- package/work-view/crates/cli/src/scope.rs +157 -0
- package/work-view/crates/cli/src/stale.rs +581 -0
- package/work-view/crates/cli/tests/board-js/dependency-table.test.mjs +64 -0
- package/work-view/crates/cli/tests/board-js/expanded-browsing.test.mjs +79 -0
- package/work-view/crates/cli/tests/board-js/harness-smoke.test.mjs +11 -0
- package/work-view/crates/cli/tests/board-js/harness.mjs +437 -0
- package/work-view/crates/cli/tests/board-js/kanban-detail.test.mjs +75 -0
- package/work-view/crates/cli/tests/board-js/markdown-filter.test.mjs +60 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/CONVENTIONS.md +3 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/epics/epic-alpha.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-a.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-b.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-alpha-1.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-research-1.md +19 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-done.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-shipped.md +18 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/backlog/idea-backlog.md +7 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/CONVENTIONS.md +6 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-dep-done.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-design-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-impl-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/stories/story-review-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/CONVENTIONS.md +4 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/epics/malformed-no-closing-fence.md +8 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/features/good-item.md +16 -0
- package/work-view/crates/cli/tests/integration.rs +2833 -0
- package/work-view/crates/core/Cargo.toml +13 -0
- package/work-view/crates/core/src/error.rs +61 -0
- package/work-view/crates/core/src/filter.rs +804 -0
- package/work-view/crates/core/src/graph.rs +324 -0
- package/work-view/crates/core/src/index.rs +590 -0
- package/work-view/crates/core/src/lib.rs +54 -0
- package/work-view/crates/core/src/model.rs +202 -0
- package/work-view/crates/core/src/parse.rs +494 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/CONVENTIONS.md +15 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/epics/epic-core.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-parser.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-query.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/malformed-no-id.md +15 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-parse-unit.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-security-gate.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-old.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-parser.md +17 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/backlog/idea-future-board.md +7 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/CONVENTIONS.md +10 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/archive/dup-item.md +18 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/releases/v2.0/dup-item.md +18 -0
- package/work-view/crates/core/tests/integration.rs +541 -0
- package/work-view/dist/.gitattributes +1 -0
- package/work-view/dist/README.md +42 -0
- package/work-view/dist/aarch64-apple-darwin/work-view +0 -0
- package/work-view/dist/aarch64-unknown-linux-musl/work-view +0 -0
- package/work-view/dist/x86_64-apple-darwin/work-view +0 -0
- package/work-view/dist/x86_64-unknown-linux-musl/work-view +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Approximation & Precomputation — Perf-Scout Strategy Lens
|
|
2
|
+
|
|
3
|
+
> **When to load this lens**: anywhere an *exact* answer is computed where an
|
|
4
|
+
> *approximate* one would do, membership/cardinality/quantile/frequency queries,
|
|
5
|
+
> similarity search, or expensive results that could be computed ahead of time.
|
|
6
|
+
>
|
|
7
|
+
> **Where these ideas come from**: streaming algorithms, big-data analytics
|
|
8
|
+
> (sketches), information retrieval (ANN), and the precomputation school. The core
|
|
9
|
+
> move is radical: *change what counts as a correct answer*. Trading a little
|
|
10
|
+
> accuracy (often <1% error) can buy orders of magnitude — but only where the
|
|
11
|
+
> domain tolerates it, so each candidate must name the accuracy it gives up and
|
|
12
|
+
> who's okay with that. Every entry is a *candidate* hypothesis, not a proven win.
|
|
13
|
+
> Cite `file:line`, attribute the source, give a validation path.
|
|
14
|
+
|
|
15
|
+
## Detection signals (where this lens might apply — not proof)
|
|
16
|
+
- Exact distinct-count / cardinality over a large stream (`COUNT(DISTINCT ...)`, dedup-to-count).
|
|
17
|
+
- Exact membership tests against a huge set on a slow/large store.
|
|
18
|
+
- Top-k / heavy-hitters / frequency over a stream computed by full tallies.
|
|
19
|
+
- Exact quantiles/percentiles over large data (sort-then-index).
|
|
20
|
+
- Nearest-neighbor / similarity search done by comparing against all items.
|
|
21
|
+
- Expensive deterministic functions over a bounded input domain (table-able).
|
|
22
|
+
- "Good enough" metrics (dashboards, recommendations, monitoring) computed exactly.
|
|
23
|
+
|
|
24
|
+
## Strategies
|
|
25
|
+
|
|
26
|
+
### 1. HyperLogLog for cardinality
|
|
27
|
+
- **Borrowed from**: streaming analytics (Flajolet et al. 2007; Redis, Presto, BigQuery `APPROX_COUNT_DISTINCT`).
|
|
28
|
+
- **The idea**: estimate distinct counts in kilobytes of state with a standard error of ~1.04/√m (m = registers — e.g. ~2% at 2048 registers, ~1.5 kB) instead of holding every distinct value to count exactly.
|
|
29
|
+
- **Code signals**: building a big set/map just to take its size; `COUNT(DISTINCT)` over huge data; dedup-for-counting.
|
|
30
|
+
- **Speculative win**: huge memory reduction; mergeable across shards.
|
|
31
|
+
- **Cost / risk**: relative error scales as 1/√m, so halving error costs 4x state; not for exact-count requirements.
|
|
32
|
+
- **Validate by**: error vs. exact on a sample; memory before/after — profile via perf-design.
|
|
33
|
+
|
|
34
|
+
### 2. Bloom filter for membership
|
|
35
|
+
- **Borrowed from**: databases, caches, networking.
|
|
36
|
+
- **The idea**: a compact probabilistic set answers "definitely not present / probably present" so most true-negatives skip the expensive exact check.
|
|
37
|
+
- **Code signals**: membership tests against a large/slow set; existence checks before costly fetches (LSM SSTable probes, cache miss guards).
|
|
38
|
+
- **Speculative win**: removes most negative lookups from the slow path; ~10 bits/key for ~1% false-positive rate.
|
|
39
|
+
- **Cost / risk**: false positives only (never false negatives), tunable by bits/key and hash count; classic Bloom has no deletes (a counting Bloom adds deletes at 1.5-4x space).
|
|
40
|
+
- **Validate by**: false-positive rate vs. configured target; slow-path hits avoided.
|
|
41
|
+
|
|
42
|
+
### 3. Cuckoo or ribbon filter as a Bloom alternative
|
|
43
|
+
- **Borrowed from**: networking / databases — *cuckoo filter* (Fan et al. 2014; Redis), a different construction from *ribbon filter* (RocksDB, an XOR/linear-system-based static filter).
|
|
44
|
+
- **The idea**: two distinct space-saving alternatives to Bloom. A **cuckoo filter** stores fingerprints in a cuckoo-hash table and adds native O(1) deletes, beating space-optimized Bloom below ~3% target FPR. A **ribbon filter** solves a small XOR linear system to build a *static, immutable* filter ~30% smaller than Bloom at the same FPR, at higher build CPU — not the same family as cuckoo, grouped here only as a low-FPR Bloom substitute.
|
|
45
|
+
- **Code signals**: a Bloom filter that also needs deletes (currently rebuilt or shadowed by tombstones) → cuckoo; a read-only/static membership set wanting minimum bits/key → ribbon.
|
|
46
|
+
- **Speculative win**: deletes without a rebuild (cuckoo), or tighter space than Bloom at low FPR (both) — *if* the workload matches the filter's mutability model.
|
|
47
|
+
- **Cost / risk**: cuckoo inserts can fail when near-full (needs headroom/resize); ribbon is build-once and not incrementally mutable, with extra build cost.
|
|
48
|
+
- **Validate by**: FPR + space vs. an equivalent Bloom at the same target; delete-path correctness for cuckoo.
|
|
49
|
+
|
|
50
|
+
### 4. Count-Min Sketch for frequency estimation
|
|
51
|
+
- **Borrowed from**: streaming algorithms (Cormode & Muthukrishnan 2005), network telemetry.
|
|
52
|
+
- **The idea**: estimate the frequency of a *queried* item in sublinear space instead of an exact per-item counter map. Note: CMS answers "how often did I see X?"; it does **not** enumerate the heaviest items on its own — to get top-k you pair it with a heap of candidate keys, or use a counter-based summary like SpaceSaving (next entry).
|
|
53
|
+
- **Code signals**: large frequency maps for rate limiting / trending / per-key counts at scale; needing a point-frequency lookup, not the full ranking.
|
|
54
|
+
- **Speculative win**: bounded memory regardless of key count; mergeable across shards.
|
|
55
|
+
- **Cost / risk**: overestimates only, never underestimates (collisions inflate counts); error bounded by ε·N with probability 1−δ; no top-k without a companion structure.
|
|
56
|
+
- **Validate by**: estimate error vs. exact counts on a sample; memory.
|
|
57
|
+
|
|
58
|
+
### 5. SpaceSaving for top-k / heavy hitters
|
|
59
|
+
- **Borrowed from**: streaming algorithms (Metwally, Agrawal & El Abbadi 2005; "Stream-Summary").
|
|
60
|
+
- **The idea**: track only k counters, evicting the current minimum, to surface the top-k frequent items directly — a counter-based alternative to a sketch when you want the *items*, not per-key frequency lookups.
|
|
61
|
+
- **Code signals**: full per-key tally sorted at the end to take top-k; trending/leaderboard over an unbounded key space.
|
|
62
|
+
- **Speculative win**: O(k) memory independent of distinct-key count; O(1) update.
|
|
63
|
+
- **Cost / risk**: tracked counts are upper bounds (with a known per-counter error margin); rare items below the k-th counter are invisible.
|
|
64
|
+
- **Validate by**: recovered top-k vs. exact top-k on a sample (precision/recall of the set); memory.
|
|
65
|
+
|
|
66
|
+
### 6. Approximate quantiles (t-digest / KLL / GK)
|
|
67
|
+
- **Borrowed from**: monitoring/metrics systems, streaming statistics (Greenwald-Khanna 2001; KLL 2016; Dunning's t-digest).
|
|
68
|
+
- **The idea**: estimate percentiles in small mergeable state instead of sorting/holding all samples.
|
|
69
|
+
- **Code signals**: collecting all latencies/values to compute p50/p95/p99; sort-to-percentile over large data.
|
|
70
|
+
- **Speculative win**: bounded memory; streaming and mergeable across nodes.
|
|
71
|
+
- **Cost / risk**: pick the sketch by where you need accuracy — t-digest concentrates accuracy at the tails (good for p99) but has no worst-case bound; KLL/GK give uniform rank-error guarantees instead.
|
|
72
|
+
- **Validate by**: quantile error vs. exact at the percentiles you actually report; memory.
|
|
73
|
+
|
|
74
|
+
### 7. Approximate nearest neighbor (ANN)
|
|
75
|
+
- **Borrowed from**: vector search / IR (HNSW, IVF, LSH; FAISS, ScaNN, DiskANN).
|
|
76
|
+
- **The idea**: index vectors for approximate similarity search so queries examine a small candidate set instead of comparing against every item; product quantization (IVF+PQ) compresses vectors for memory, DiskANN serves billion-scale from SSD.
|
|
77
|
+
- **Code signals**: brute-force similarity/distance over all items per query; full-scan k-NN.
|
|
78
|
+
- **Speculative win**: sublinear query time at 90%+ recall; scales to millions–billions of vectors.
|
|
79
|
+
- **Cost / risk**: recall is the accuracy traded (tunable via ef/nprobe); PQ compression lowers recall@1 further; index build time and memory.
|
|
80
|
+
- **Validate by**: recall@k vs. exact brute force on a query set; query latency and index memory.
|
|
81
|
+
|
|
82
|
+
### 8. Sampling instead of full computation
|
|
83
|
+
- **Borrowed from**: approximate query processing (BlinkDB), statistics.
|
|
84
|
+
- **The idea**: compute on a representative sample and extrapolate (with confidence bounds) for metrics that tolerate it.
|
|
85
|
+
- **Code signals**: full-dataset aggregation for a dashboard/estimate that doesn't need exactness.
|
|
86
|
+
- **Speculative win**: cost scales with sample size, not dataset size (BlinkDB reported 2-10% error at 200x speedup).
|
|
87
|
+
- **Cost / risk**: sampling error and bias; rare groups / high-variance aggregates need stratified samples.
|
|
88
|
+
- **Validate by**: estimate vs. full result on history; confidence-interval coverage check.
|
|
89
|
+
|
|
90
|
+
### 9. Reservoir sampling for a fixed-size stream sample
|
|
91
|
+
- **Borrowed from**: streaming statistics (Vitter's Algorithm R, 1985).
|
|
92
|
+
- **The idea**: maintain a uniform random sample of fixed size k from an unbounded stream in one pass, without knowing the length in advance — feeds any downstream estimate (quantiles, distinct-ish, debugging traces).
|
|
93
|
+
- **Code signals**: buffering an entire stream to later pick a random subset; "keep the last N then sample" approximations that bias toward recent items.
|
|
94
|
+
- **Speculative win**: O(k) memory, single pass, unbiased over the whole stream.
|
|
95
|
+
- **Cost / risk**: a uniform sample under-represents rare events; weighted/time-decayed variants needed if recency matters.
|
|
96
|
+
- **Validate by**: sample-derived estimate vs. exact on a replayed stream; distribution check vs. uniform.
|
|
97
|
+
|
|
98
|
+
### 10. Precomputed lookup tables
|
|
99
|
+
- **Borrowed from**: classic table-driven optimization (trig tables, CRC tables, color LUTs).
|
|
100
|
+
- **The idea**: precompute results for a bounded input domain into a table and replace runtime computation with an array index.
|
|
101
|
+
- **Code signals**: an expensive deterministic function over a small/bounded input range called hot and often.
|
|
102
|
+
- **Speculative win**: computation → a single memory load.
|
|
103
|
+
- **Cost / risk**: table memory; cache footprint; only for bounded domains.
|
|
104
|
+
- **Validate by**: lookup vs. compute benchmark; table size vs. cache.
|
|
105
|
+
|
|
106
|
+
### 11. Perfect / minimal hashing for static key sets
|
|
107
|
+
- **Borrowed from**: compilers (keyword lookup, `gperf` for small sets; CMPH/CHD, BBHash for massive sets).
|
|
108
|
+
- **The idea**: for a known static set of keys, build a (minimal) perfect hash so lookups are collision-free O(1) with no probing and minimal memory.
|
|
109
|
+
- **Code signals**: hash lookups against a fixed set of keys (keywords, opcodes, config); startup-built immutable maps.
|
|
110
|
+
- **Speculative win**: collision-free single-probe lookups; the function itself is compact (CHD ~2.07 bits/key, BBHash ~3 bits/key — separate from value storage).
|
|
111
|
+
- **Cost / risk**: only for static key sets; build step; the MPHF returns a slot for any input, so non-keys need a separate verification check.
|
|
112
|
+
- **Validate by**: lookup benchmark vs. general hash map; function memory.
|
|
113
|
+
|
|
114
|
+
### 12. Incremental view maintenance (precompute + update)
|
|
115
|
+
- **Borrowed from**: materialized views; differential dataflow (Timely/Materialize), DBSP.
|
|
116
|
+
- **The idea**: maintain a precomputed result incrementally as inputs change, rather than recomputing — straddles precomputation and incrementalization for derived data.
|
|
117
|
+
- **Code signals**: recomputing a derived dataset from scratch on each input change.
|
|
118
|
+
- **Speculative win**: update cost proportional to the change, not the dataset.
|
|
119
|
+
- **Cost / risk**: incremental logic complexity; correctness vs. full recompute.
|
|
120
|
+
- **Validate by**: property-test incremental == full recompute; benchmark with realistic change rates.
|
|
121
|
+
|
|
122
|
+
### 13. Succinct / compressed structures queried in place
|
|
123
|
+
- **Borrowed from**: succinct data structures (wavelet trees, FM-index), bioinformatics, search.
|
|
124
|
+
- **The idea**: store data in a near-information-theoretic-minimum form that still answers queries (rank/select on bit vectors in O(1) with o(n) extra bits, prefix) without decompression, fitting far more in memory/cache.
|
|
125
|
+
- **Code signals**: large indexes/bitmaps/text that must be queried but barely fit in memory.
|
|
126
|
+
- **Speculative win**: dramatic memory reduction while staying queryable; more fits in cache.
|
|
127
|
+
- **Cost / risk**: implementation complexity; constant-factor query overhead vs. a plain structure.
|
|
128
|
+
- **Validate by**: memory footprint; query benchmark vs. plain structure.
|
|
129
|
+
|
|
130
|
+
### 14. Tolerate staleness (cheaper freshness)
|
|
131
|
+
- **Borrowed from**: caching, eventually-consistent reads, monitoring.
|
|
132
|
+
- **The idea**: serve a slightly stale precomputed answer and refresh in the background where the domain doesn't need up-to-the-instant accuracy.
|
|
133
|
+
- **Code signals**: recomputing fresh values per request for data that changes slowly or tolerates lag.
|
|
134
|
+
- **Speculative win**: removes recomputation from the request path.
|
|
135
|
+
- **Cost / risk**: staleness window must be acceptable to the use case.
|
|
136
|
+
- **Validate by**: request latency; staleness tolerance confirmation.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Caching & Memoization — Perf-Scout Strategy Lens
|
|
2
|
+
|
|
3
|
+
> **When to load this lens**: repeated identical computation or requests,
|
|
4
|
+
> expensive pure functions, hot read paths, derived values recomputed per call,
|
|
5
|
+
> chatty reads against a slow backing store.
|
|
6
|
+
>
|
|
7
|
+
> **Where these ideas come from**: CPU cache hierarchies, CDNs, database buffer
|
|
8
|
+
> pools and materialized views, and web-scale read paths. Caching is a force
|
|
9
|
+
> multiplier *and* a footgun — it trades freshness and invalidation complexity for
|
|
10
|
+
> speed, so each candidate must name what it caches, how it's keyed, and how it's
|
|
11
|
+
> invalidated. Every entry is a *candidate* hypothesis, not a proven win. Cite
|
|
12
|
+
> `file:line`, attribute the source, give a validation path.
|
|
13
|
+
>
|
|
14
|
+
> **Honest caveat to carry into every idea**: caching can hide an underlying
|
|
15
|
+
> algorithmic problem rather than fix it. Prefer "do less work" (algorithmic lens)
|
|
16
|
+
> when the work is reducible; reach for caching when the work is genuinely
|
|
17
|
+
> irreducible and reused.
|
|
18
|
+
|
|
19
|
+
## Detection signals (where this lens might apply — not proof)
|
|
20
|
+
- The same expensive call/query/computation repeated with identical inputs.
|
|
21
|
+
- Hot read paths hitting a slow backing store (DB, network, disk) for stable data.
|
|
22
|
+
- Derived/computed values (formatting, parsing, aggregation) recomputed every call.
|
|
23
|
+
- Many concurrent misses for the same key (stampede risk).
|
|
24
|
+
- Read-mostly data with rare writes.
|
|
25
|
+
- Per-request recomputation of values that change far slower than request rate.
|
|
26
|
+
|
|
27
|
+
## Strategies
|
|
28
|
+
|
|
29
|
+
### 1. Memoize expensive pure functions
|
|
30
|
+
- **Borrowed from**: dynamic programming, functional caching.
|
|
31
|
+
- **The idea**: wrap a deterministic, expensive function in a keyed cache; return stored results on repeat inputs.
|
|
32
|
+
- **Code signals**: a pure function (parse, compile, format, hash, derive) called repeatedly with a small distinct-input set.
|
|
33
|
+
- **Speculative win**: one computation per distinct input instead of per call.
|
|
34
|
+
- **Cost / risk**: unbounded growth without eviction; key must capture all inputs.
|
|
35
|
+
- **Validate by**: hit rate on real input distribution.
|
|
36
|
+
|
|
37
|
+
### 2. Tiered cache (L1 in-process → L2 shared → origin)
|
|
38
|
+
- **Borrowed from**: CPU cache hierarchy, CDN edge tiers.
|
|
39
|
+
- **The idea**: front a slow origin with a fast small in-process cache, backed by a larger shared cache (Redis/memcached), backed by the origin.
|
|
40
|
+
- **Code signals**: every read hits the DB/network for data reused across requests.
|
|
41
|
+
- **Speculative win**: most reads served from the fastest tier; origin load drops sharply.
|
|
42
|
+
- **Cost / risk**: coherence across tiers; invalidation fan-out; staleness windows.
|
|
43
|
+
- **Validate by**: per-tier hit rates; origin QPS before/after.
|
|
44
|
+
|
|
45
|
+
### 3. Request coalescing / single-flight
|
|
46
|
+
- **Borrowed from**: `singleflight` (Go), Caffeine `AsyncLoadingCache`, CDN collapse-forwarding.
|
|
47
|
+
- **The idea**: when many concurrent callers miss the same key, let one compute and have the rest await its result instead of all hitting the origin.
|
|
48
|
+
- **Code signals**: `if miss: load_from_origin()` with no in-flight dedup; thundering-herd risk on cold keys.
|
|
49
|
+
- **Speculative win**: collapses N concurrent identical loads to 1; protects the origin under spikes.
|
|
50
|
+
- **Cost / risk**: shared failure (one error fails all waiters); modest coordination.
|
|
51
|
+
- **Validate by**: origin call count under concurrent load; spike test.
|
|
52
|
+
|
|
53
|
+
### 4. Materialized views / precomputed aggregates
|
|
54
|
+
- **Borrowed from**: database materialized views, OLAP cubes.
|
|
55
|
+
- **The idea**: precompute and store expensive aggregates/joins; refresh on write or schedule; serve reads from the precomputed form.
|
|
56
|
+
- **Code signals**: dashboards/summaries recomputed from raw rows on every read; heavy group-by per request.
|
|
57
|
+
- **Speculative win**: read cost drops to a lookup; compute moves off the read path.
|
|
58
|
+
- **Cost / risk**: refresh strategy and staleness; storage.
|
|
59
|
+
- **Validate by**: read latency and origin load; staleness tolerance check.
|
|
60
|
+
|
|
61
|
+
### 5. Right eviction policy for the access pattern
|
|
62
|
+
- **Borrowed from**: OS page replacement and DB buffer pools — LRU, LFU, CLOCK, ARC (Megiddo & Modha, FAST 2003: recency+frequency with ghost lists, scan-resistant, self-tuning), and modern scan-resistant designs: W-TinyLFU (Caffeine — an admission filter over a count-min frequency sketch guarding a Segmented-LRU main region), S3-FIFO (SOSP 2023 — three FIFO queues with quick demotion of one-hit objects), SIEVE (NSDI 2024 — a single FIFO with a lazily-advancing "hand", simpler than LRU).
|
|
63
|
+
- **The idea**: match the eviction policy to the workload — LRU for recency, LFU/W-TinyLFU for skewed popularity, ARC/S3-FIFO/SIEVE for scan resistance — instead of a naive map that grows unbounded or evicts blindly.
|
|
64
|
+
- **Code signals**: an unbounded map used as a cache, or a plain LRU under a scan-heavy/skewed workload with a poor hit rate.
|
|
65
|
+
- **Speculative win**: higher hit rate at the same memory; scan resistance; some FIFO-based policies (S3-FIFO, SIEVE) also report better lock-free throughput than LRU.
|
|
66
|
+
- **Cost / risk**: policy complexity; wrong policy can underperform plain LRU; many libraries only ship LRU.
|
|
67
|
+
- **Validate by**: simulate hit rate on real traces across policies (libCacheSim / Caffeine simulator); compare against current policy.
|
|
68
|
+
|
|
69
|
+
### 6. Negative caching
|
|
70
|
+
- **Borrowed from**: DNS, web caches.
|
|
71
|
+
- **The idea**: cache "not found"/empty results (with a short TTL) so repeated misses for absent keys don't re-hit the origin.
|
|
72
|
+
- **Code signals**: repeated lookups for keys that frequently don't exist, each hitting the DB.
|
|
73
|
+
- **Speculative win**: removes origin load from miss-storms on absent keys.
|
|
74
|
+
- **Cost / risk**: serving stale "absent" after a create; needs short TTL or invalidation.
|
|
75
|
+
- **Validate by**: miss-rate composition; origin load on absent keys.
|
|
76
|
+
|
|
77
|
+
### 7. Compute-once / hoist derived values
|
|
78
|
+
- **Borrowed from**: loop-invariant code motion.
|
|
79
|
+
- **The idea**: compute a value once and reuse it instead of recomputing per iteration/request — hoist invariants out of loops, cache per-request derived state.
|
|
80
|
+
- **Code signals**: identical derivation inside a loop or repeated per call within one request.
|
|
81
|
+
- **Speculative win**: removes redundant computation proportional to iteration/call count.
|
|
82
|
+
- **Cost / risk**: minimal; ensure the value is truly invariant for the scope.
|
|
83
|
+
- **Validate by**: benchmark; trace recomputation count.
|
|
84
|
+
|
|
85
|
+
### 8. Content-addressed reuse
|
|
86
|
+
- **Borrowed from**: content-addressed storage (Git, build caches), deduplication.
|
|
87
|
+
- **The idea**: key results by a hash of their inputs so identical work anywhere reuses the same cached artifact (build outputs, rendered fragments, compiled queries).
|
|
88
|
+
- **Code signals**: the same artifact regenerated from identical inputs across requests/builds.
|
|
89
|
+
- **Speculative win**: global dedup of identical work; cross-request/cross-build reuse.
|
|
90
|
+
- **Cost / risk**: hashing cost; cache storage and GC.
|
|
91
|
+
- **Validate by**: dedup/hit rate on real inputs.
|
|
92
|
+
|
|
93
|
+
### 9. Write-through vs. write-back choice
|
|
94
|
+
- **Borrowed from**: CPU caches, DB buffer management.
|
|
95
|
+
- **The idea**: pick the write policy deliberately — write-back (buffer and flush in batches) for write-heavy paths that tolerate a durability window; write-through for simplicity/consistency.
|
|
96
|
+
- **Code signals**: every write synchronously hits the slow store; write amplification on a hot path.
|
|
97
|
+
- **Speculative win**: batched/deferred writes cut per-write origin cost.
|
|
98
|
+
- **Cost / risk**: durability/consistency window; crash recovery complexity.
|
|
99
|
+
- **Validate by**: write throughput; correctness under crash scenarios.
|
|
100
|
+
|
|
101
|
+
### 10. Probabilistic early expiration (anti-stampede TTL)
|
|
102
|
+
- **Borrowed from**: XFetch (Vattani, Chierichetti & Lowenstein, "Optimal Probabilistic Cache Stampede Prevention", VLDB 2015).
|
|
103
|
+
- **The idea**: on each read, recompute early with a probability that rises as TTL nears (scaled by recompute cost), so a popular key doesn't expire for everyone at once — no locks or coordination needed.
|
|
104
|
+
- **Code signals**: synchronized expiry of popular keys causing periodic origin spikes.
|
|
105
|
+
- **Speculative win**: smooths refresh load; avoids cliff-edge stampedes without a single-flight lock.
|
|
106
|
+
- **Cost / risk**: a little early recomputation; the beta/cost factor needs tuning.
|
|
107
|
+
- **Validate by**: origin load over time around expiries; compare against fixed-TTL baseline.
|
|
108
|
+
|
|
109
|
+
### 11. Stale-while-revalidate (serve stale, refresh in background)
|
|
110
|
+
- **Borrowed from**: HTTP `stale-while-revalidate` (RFC 5861), CDN serve-stale.
|
|
111
|
+
- **The idea**: once an entry passes its freshness window, keep serving the stale value to readers while a single background refresh updates it — readers never block on the origin during revalidation.
|
|
112
|
+
- **Code signals**: reads block synchronously on refresh at expiry; latency spikes correlate with TTL boundaries; data tolerates brief staleness.
|
|
113
|
+
- **Speculative win**: refresh latency disappears from the read path; tail latency at expiry flattens.
|
|
114
|
+
- **Cost / risk**: readers may see stale data within the window; needs dedup so only one refresh runs (pairs with single-flight).
|
|
115
|
+
- **Validate by**: p99 read latency around expiry; staleness-window tolerance check; origin refresh count.
|
|
116
|
+
|
|
117
|
+
### 12. Cache key normalization
|
|
118
|
+
- **Borrowed from**: CDN cache keys (query-string sort, `No-Vary-Search`), HTTP `Vary` discipline.
|
|
119
|
+
- **The idea**: canonicalize keys before lookup — sort/strip irrelevant query params, lowercase, drop tracking tokens, narrow `Vary` — so semantically identical requests collapse to one entry instead of fragmenting the cache.
|
|
120
|
+
- **Code signals**: cache key built from a raw URL/struct with parameter order or noise fields included; low hit rate despite repeated logical requests; `Vary: Cookie`-style over-keying.
|
|
121
|
+
- **Speculative win**: fragmented entries collapse into shared hits; higher hit rate at the same data, no policy change.
|
|
122
|
+
- **Cost / risk**: over-normalization can collapse requests that should differ (correctness bug — must verify which fields are semantically load-bearing).
|
|
123
|
+
- **Validate by**: hit rate before/after on real key distribution; audit that dropped fields don't change the response.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Compiler, Runtime & Language — Perf-Scout Strategy Lens
|
|
2
|
+
|
|
3
|
+
> **When to load this lens**: hot inner loops, allocation/GC pressure, dynamic
|
|
4
|
+
> dispatch, boxing/autoboxing, interpreters, string building, reflection, heavy
|
|
5
|
+
> abstraction layers on a hot path.
|
|
6
|
+
>
|
|
7
|
+
> **Where these ideas come from**: compilers, language runtimes (JVM, V8, CLR, Go
|
|
8
|
+
> runtime), and low-level systems programming. These are the smaller-constant-
|
|
9
|
+
> factor wins — they rarely change the complexity class, but on a hot path they
|
|
10
|
+
> compound. Reach here after the algorithmic, locality, and I/O lenses. Every
|
|
11
|
+
> entry is a *candidate* hypothesis, not a proven win. Cite `file:line`, attribute
|
|
12
|
+
> the source, give a validation path.
|
|
13
|
+
|
|
14
|
+
## Detection signals (where this lens might apply — not proof)
|
|
15
|
+
- Allocation inside a tight loop; high GC frequency or pause time.
|
|
16
|
+
- Virtual/interface/dynamic dispatch in a hot loop (polymorphic `update()`/`next()`).
|
|
17
|
+
- Boxing of primitives into objects (collections of `Integer`/`Object`, autoboxing).
|
|
18
|
+
- String building via repeated concatenation; many short-lived intermediate strings.
|
|
19
|
+
- Reflection / dynamic lookups on a hot path.
|
|
20
|
+
- Heavy generic abstraction (iterators of iterators, deep call chains) per element.
|
|
21
|
+
- An interpreter / dispatch loop over an instruction or AST stream.
|
|
22
|
+
- A CPU-bound binary built with only default optimization (no PGO/LTO); hot calls that cross module/crate boundaries and never inline.
|
|
23
|
+
|
|
24
|
+
## Strategies
|
|
25
|
+
|
|
26
|
+
### 1. Cut allocations on the hot path
|
|
27
|
+
- **Borrowed from**: GC-runtime tuning, systems programming.
|
|
28
|
+
- **The idea**: reuse buffers, allocate outside the loop, use value types/stack allocation, and avoid intermediate objects so the allocator/GC isn't on the hot path.
|
|
29
|
+
- **Code signals**: `new`/slice/map allocation per iteration; temporary collections created and discarded.
|
|
30
|
+
- **Speculative win**: lower GC pressure and pause frequency; better locality.
|
|
31
|
+
- **Cost / risk**: lifetime discipline; can hurt readability.
|
|
32
|
+
- **Validate by**: allocations/op and GC metrics — perf-design.
|
|
33
|
+
|
|
34
|
+
### 2. Devirtualize / monomorphize hot dispatch
|
|
35
|
+
- **Borrowed from**: JIT inline caches (V8 IC sites stay fast while monomorphic, degrade as they become polymorphic and fall off the fast path once megamorphic — V8 caps the polymorphic IC at ~4 shapes), Rust/C++ template monomorphization.
|
|
36
|
+
- **The idea**: keep hot call sites monomorphic, or replace virtual/interface dispatch with concrete types, generics/monomorphization, or sealed-type switches so the call inlines and predicts.
|
|
37
|
+
- **Code signals**: polymorphic method called per element; a hot call site that sees many shapes/types (megamorphic); trait-object/`dyn` loops.
|
|
38
|
+
- **Speculative win**: enables inlining, removes indirect-call and branch-mispredict cost.
|
|
39
|
+
- **Cost / risk**: monomorphization can bloat code and *hurt* via instruction-cache misses; less flexible.
|
|
40
|
+
- **Validate by**: inlining/devirt evidence (e.g. JIT logs, `--emit=asm`); loop microbenchmark.
|
|
41
|
+
|
|
42
|
+
### 3. Avoid boxing / use primitive specializations
|
|
43
|
+
- **Borrowed from**: type-specific primitive collections (fastutil, Trove, HPPC); Go/Rust value types.
|
|
44
|
+
- **The idea**: store primitives in primitive arrays/specialized collections (e.g. fastutil `IntArrayList`/`Int2IntOpenHashMap`) instead of boxed object collections; avoid autoboxing in hot math.
|
|
45
|
+
- **Code signals**: `List<Integer>`/`Map<Integer,...>`, autoboxing in loops, `interface{}`/`Object` numeric storage.
|
|
46
|
+
- **Speculative win**: removes per-element allocation and pointer indirection; denser, more cache-friendly layout.
|
|
47
|
+
- **Cost / risk**: extra dependency and specialized APIs; less generic.
|
|
48
|
+
- **Validate by**: allocations/op and loop benchmark.
|
|
49
|
+
|
|
50
|
+
### 4. Efficient string building / avoid intermediate strings
|
|
51
|
+
- **Borrowed from**: runtime string-builder idioms.
|
|
52
|
+
- **The idea**: use a builder/buffer (with capacity reserved) instead of `+=`; avoid creating throwaway substrings; work on byte/char slices; intern repeated strings.
|
|
53
|
+
- **Code signals**: string `+=` in a loop; many `substring`/`split`/`format` temporaries on a hot path.
|
|
54
|
+
- **Speculative win**: removes quadratic concatenation and allocation churn.
|
|
55
|
+
- **Cost / risk**: minimal; readability tradeoff.
|
|
56
|
+
- **Validate by**: allocations and timing on the build path.
|
|
57
|
+
|
|
58
|
+
### 5. Hoist work out of the loop / strength reduction
|
|
59
|
+
- **Borrowed from**: classic compiler optimizations (LICM, strength reduction).
|
|
60
|
+
- **The idea**: move loop-invariant computation out of the loop; replace expensive ops with cheaper equivalents (multiply→add, divide→shift/mul-by-reciprocal, pow→repeated mul).
|
|
61
|
+
- **Code signals**: invariant expressions recomputed each iteration; expensive arithmetic in the inner loop.
|
|
62
|
+
- **Speculative win**: removes redundant per-iteration work.
|
|
63
|
+
- **Cost / risk**: minimal; watch numeric equivalence.
|
|
64
|
+
- **Validate by**: loop microbenchmark.
|
|
65
|
+
|
|
66
|
+
### 6. Branchless / predictable control flow
|
|
67
|
+
- **Borrowed from**: HFT, codecs, SIMD programming.
|
|
68
|
+
- **The idea**: remove unpredictable branches in tight loops (use conditional moves, masks, table lookups, or sort to make branches predictable) to avoid misprediction stalls.
|
|
69
|
+
- **Code signals**: data-dependent branches in a hot loop over unsorted data; per-element `if` on random conditions.
|
|
70
|
+
- **Speculative win**: fewer branch mispredictions; enables vectorization.
|
|
71
|
+
- **Cost / risk**: branchless code can be less clear and sometimes slower; measure.
|
|
72
|
+
- **Validate by**: branch-miss counters; benchmark both forms.
|
|
73
|
+
|
|
74
|
+
### 7. Specialize the hot path (partial evaluation)
|
|
75
|
+
- **Borrowed from**: partial evaluation / Futamura projections (specialize a general routine against its known inputs — the model behind Truffle/GraalVM and PyPy/RPython JITs), template metaprogramming.
|
|
76
|
+
- **The idea**: generate or select a specialized version of a general routine for the common case (fixed config, known sizes, common types) instead of running the fully general path every time.
|
|
77
|
+
- **Code signals**: a general function with config/flags re-interpreted per call; an interpreter re-dispatching the same program.
|
|
78
|
+
- **Speculative win**: removes per-call generality overhead; common case runs lean.
|
|
79
|
+
- **Cost / risk**: code duplication; specialization machinery to build and maintain.
|
|
80
|
+
- **Validate by**: common-case microbenchmark vs. the general path.
|
|
81
|
+
|
|
82
|
+
### 8. Lazy evaluation / avoid eager materialization
|
|
83
|
+
- **Borrowed from**: lazy languages, iterator/stream pipelines.
|
|
84
|
+
- **The idea**: defer computation until needed and stream through transforms instead of building full intermediate collections.
|
|
85
|
+
- **Code signals**: chained `map/filter` materializing each stage; computing values that may not be used.
|
|
86
|
+
- **Speculative win**: skips unused work; lower peak memory.
|
|
87
|
+
- **Cost / risk**: laziness hides cost and complicates debugging; thunk overhead.
|
|
88
|
+
- **Validate by**: fraction of results consumed; memory and timing.
|
|
89
|
+
|
|
90
|
+
### 9. Copy-on-write / move semantics / avoid deep copies
|
|
91
|
+
- **Borrowed from**: persistent/COW data structures, Rust ownership/moves, C++ move semantics.
|
|
92
|
+
- **The idea**: pass by reference/move, share immutable data, and copy only on mutation instead of defensive deep copies.
|
|
93
|
+
- **Code signals**: large structures deep-copied when passed; defensive clones of read-only data.
|
|
94
|
+
- **Speculative win**: removes large copies and their allocations.
|
|
95
|
+
- **Cost / risk**: aliasing/ownership bugs if mutation leaks; naive COW can *backfire* under concurrency — the atomic refcount became a bottleneck, which is why C++11 forbids COW `std::string` (GCC moved to small-string optimization in 5.1). Prefer moves/SSO for small/contended data; reserve COW for genuinely large, rarely-mutated shares.
|
|
96
|
+
- **Validate by**: copy count/size; benchmark, including the concurrent case.
|
|
97
|
+
|
|
98
|
+
### 10. Compile-time / startup precomputation (comptime, const-fold, codegen)
|
|
99
|
+
- **Borrowed from**: `constexpr`/Zig `comptime`, code generation, perfect-hash generators (GNU `gperf` emits a collision-free keyword lookup as C — single probe, one string compare).
|
|
100
|
+
- **The idea**: move work to compile time or startup — const-fold, generate lookup/dispatch tables, build a perfect hash for a fixed keyword set, precompile regexes/queries — so runtime just reads results.
|
|
101
|
+
- **Code signals**: recompiling regexes/parsing config/building dispatch tables on every call; linear keyword scans; constant work done at runtime.
|
|
102
|
+
- **Speculative win**: removes invariant setup from the runtime path.
|
|
103
|
+
- **Cost / risk**: build complexity; codegen maintenance; generated artifacts can drift from source if not regenerated.
|
|
104
|
+
- **Validate by**: runtime cost with precomputed artifacts vs. computed-at-runtime baseline.
|
|
105
|
+
|
|
106
|
+
### 11. Choose the right runtime construct
|
|
107
|
+
- **Borrowed from**: runtime/stdlib performance lore; cache-friendly hash maps (Abseil Swiss tables — dense open-addressing with a SIMD-scanned control byte array; adopted by Rust `hashbrown`/`std` and Go 1.24 maps).
|
|
108
|
+
- **The idea**: use the faster-by-design construct — preallocated/`reserve`d collections, a flat/Swiss-table map over a node-based one, value-type enums over object hierarchies, native arrays over generic containers on hot paths.
|
|
109
|
+
- **Code signals**: default containers grown incrementally; generic/abstract collections where a concrete one fits; node-pointer maps on a lookup-heavy path.
|
|
110
|
+
- **Speculative win**: fewer reallocations and indirections; better constants and cache behavior.
|
|
111
|
+
- **Cost / risk**: small; profile to confirm the swap helps; iteration order/pointer-stability guarantees may differ.
|
|
112
|
+
- **Validate by**: microbenchmark of the construct in context.
|
|
113
|
+
|
|
114
|
+
### 12. Let escape analysis stack-allocate short-lived objects
|
|
115
|
+
- **Borrowed from**: HotSpot C2 scalar replacement of aggregates (SRA, `-XX:+EliminateAllocations`); Go's escape analysis (`-gcflags=-m`).
|
|
116
|
+
- **The idea**: keep short-lived objects from escaping (don't store them in fields/globals or leak via interfaces) so the compiler can decompose them into registers/stack slots instead of heap-allocating — no GC involvement. (Note: HotSpot does scalar replacement, not literal stack allocation.)
|
|
117
|
+
- **Code signals**: small temporaries (points, iterators, boxed values) that outlive nothing but escape via a field assignment, returned pointer, or `interface{}`.
|
|
118
|
+
- **Speculative win**: removes heap allocations and the GC work they create on a hot path.
|
|
119
|
+
- **Cost / risk**: brittle — a small code change can make an object escape again and silently re-allocate.
|
|
120
|
+
- **Validate by**: escape/allocation logs (`-gcflags=-m`, JFR allocation profiling) and allocations/op before vs. after.
|
|
121
|
+
|
|
122
|
+
### 13. Profile-guided optimization (PGO)
|
|
123
|
+
- **Borrowed from**: feedback-directed compilation (Clang/GCC `-fprofile-use`, MSVC PGO, Go `pgo`).
|
|
124
|
+
- **The idea**: feed a representative runtime profile back into the compiler so it inlines hot callees, lays out code for the instruction cache, and separates hot from cold paths using real execution counts instead of static guesses.
|
|
125
|
+
- **Code signals**: a CPU-bound binary with a stable hot path; inlining/layout left entirely to static heuristics; large switch/dispatch where the common arm is known at runtime.
|
|
126
|
+
- **Speculative win**: better inlining and code/branch layout; vendors report single-digit-to-~double-digit-percent wins (workload-dependent — never assume the headline number).
|
|
127
|
+
- **Cost / risk**: build/CI complexity to collect and refresh profiles; a stale profile can mislead layout.
|
|
128
|
+
- **Validate by**: end-to-end benchmark of PGO vs. non-PGO build; i-cache/branch counters via perf-design.
|
|
129
|
+
|
|
130
|
+
### 14. Link-time / whole-program optimization (LTO)
|
|
131
|
+
- **Borrowed from**: interprocedural optimization (`-flto`, ThinLTO, `lto = true`).
|
|
132
|
+
- **The idea**: defer optimization to link time so the compiler sees across translation-unit boundaries and can inline cross-module calls, propagate constants, and drop dead code it otherwise can't.
|
|
133
|
+
- **Code signals**: hot calls that cross library/crate/module boundaries and never inline; thin wrappers in one TU around work in another.
|
|
134
|
+
- **Speculative win**: cross-module inlining and dead-code elimination not possible per-file; sometimes smaller binaries too.
|
|
135
|
+
- **Cost / risk**: longer link times and higher link-time memory; ThinLTO mitigates but adds build config.
|
|
136
|
+
- **Validate by**: benchmark LTO vs. non-LTO build; inspect whether the boundary call now inlines (`--emit=asm`/disasm).
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Database & Storage Internals — Perf-Scout Strategy Lens
|
|
2
|
+
|
|
3
|
+
> **When to load this lens**: custom storage/query/index code, in-memory stores,
|
|
4
|
+
> scans/filters/joins/aggregations, write-heavy paths, on-disk/serialized formats,
|
|
5
|
+
> or app code that leans heavily on a database.
|
|
6
|
+
>
|
|
7
|
+
> **Where these ideas come from**: database and storage-engine internals
|
|
8
|
+
> (Postgres, SQLite, RocksDB/LevelDB, DuckDB, ClickHouse, Lucene). These systems
|
|
9
|
+
> spent decades making "find and combine data" fast; their techniques transfer to
|
|
10
|
+
> any code that stores, indexes, scans, or joins. Every entry is a *candidate*
|
|
11
|
+
> hypothesis, not a proven win. Cite `file:line`, attribute the source, give a
|
|
12
|
+
> validation path. (For app-side query *usage* like N+1, see the I/O lens — this
|
|
13
|
+
> lens is about storage/query *internals* and patterns.)
|
|
14
|
+
|
|
15
|
+
## Detection signals (where this lens might apply — not proof)
|
|
16
|
+
- In-memory collections scanned linearly to filter/find/join on a hot path.
|
|
17
|
+
- Custom on-disk or serialized storage formats; hand-rolled indexes.
|
|
18
|
+
- Write-heavy paths doing in-place random updates.
|
|
19
|
+
- Aggregations/group-bys/joins implemented row-at-a-time in app code.
|
|
20
|
+
- Repeated membership tests against a large set, especially on a slow store.
|
|
21
|
+
- Range/prefix/sorted queries served by full scans.
|
|
22
|
+
- Large datasets where only a few columns/fields are ever touched.
|
|
23
|
+
|
|
24
|
+
## Strategies
|
|
25
|
+
|
|
26
|
+
### 1. Add the right index (and covering indexes)
|
|
27
|
+
- **Borrowed from**: B-tree/hash indexing.
|
|
28
|
+
- **The idea**: build an index matching the query's predicate (equality → hash, range/sort → ordered tree), and make it *covering* so the query is answered from the index without touching the base data.
|
|
29
|
+
- **Code signals**: linear scans filtering on a stable key; queries that read a few fields after a lookup.
|
|
30
|
+
- **Speculative win**: a scan could drop toward an O(log n)/O(1) lookup if the predicate is selective; a covering index may avoid the base-row fetch.
|
|
31
|
+
- **Cost / risk**: index memory and write-time maintenance.
|
|
32
|
+
- **Validate by**: query plan / lookup count; benchmark — perf-design.
|
|
33
|
+
|
|
34
|
+
### 2. Probabilistic membership filter before the expensive lookup
|
|
35
|
+
- **Borrowed from**: LSM-tree SSTables (RocksDB attaches a per-SSTable bloom or, since 6.15, ribbon filter), caches.
|
|
36
|
+
- **The idea**: keep a compact probabilistic membership filter so likely-absent keys skip the expensive disk/network lookup entirely. Bloom is cheapest to build; ribbon/quotient variants trade more build CPU for ~27% less memory at the same false-positive rate.
|
|
37
|
+
- **Code signals**: many lookups that miss on a slow store; checking existence before a costly fetch.
|
|
38
|
+
- **Speculative win**: removes most negative lookups from the slow path.
|
|
39
|
+
- **Cost / risk**: false positives (tunable); filter memory and build/maintenance CPU.
|
|
40
|
+
- **Validate by**: negative-lookup rate; slow-store hits before/after.
|
|
41
|
+
|
|
42
|
+
### 3. Columnar layout + vectorized execution
|
|
43
|
+
- **Borrowed from**: OLAP engines (DuckDB, ClickHouse, Arrow).
|
|
44
|
+
- **The idea**: store fields column-wise and process them in batches/vectors (DuckDB ~1–2K values, ClickHouse blocks up to 65,536) so scans touch only needed columns and run SIMD-friendly per-column kernels. Vectorized execution amortizes per-row interpreter overhead with precompiled kernels — distinct from JIT-compiling the whole query plan to machine code (an alternative model; ClickHouse adds JIT as an optional optimization, DuckDB stays vectorized).
|
|
45
|
+
- **Code signals**: row-oriented storage scanned for analytics over a few fields; aggregate/filter over many rows row-at-a-time.
|
|
46
|
+
- **Speculative win**: less data scanned, better compression, vectorizable operators.
|
|
47
|
+
- **Cost / risk**: poor fit for row-at-a-time OLTP access; row-reconstruction cost.
|
|
48
|
+
- **Validate by**: bytes/columns scanned; scan/aggregate benchmark vs row-at-a-time.
|
|
49
|
+
|
|
50
|
+
### 4. LSM-tree write batching for write-heavy paths
|
|
51
|
+
- **Borrowed from**: LevelDB/RocksDB/Cassandra.
|
|
52
|
+
- **The idea**: buffer writes in memory and flush sequentially (append-only), compacting in the background, instead of random in-place updates.
|
|
53
|
+
- **Code signals**: write-heavy workload doing random in-place updates; write amplification.
|
|
54
|
+
- **Speculative win**: could turn random writes into sequential ones and raise write throughput if the path is write-bound and read amplification stays tolerable.
|
|
55
|
+
- **Cost / risk**: read amplification and compaction overhead; tuning.
|
|
56
|
+
- **Validate by**: write throughput; read latency under compaction.
|
|
57
|
+
|
|
58
|
+
### 5. Predicate / projection pushdown
|
|
59
|
+
- **Borrowed from**: query optimizers, Parquet/columnar readers.
|
|
60
|
+
- **The idea**: apply filters and select columns as early/low as possible (at the storage/scan layer) so less data flows up the pipeline.
|
|
61
|
+
- **Code signals**: load-everything-then-filter; full objects fetched to use a subset.
|
|
62
|
+
- **Speculative win**: less data materialized, transferred, and processed.
|
|
63
|
+
- **Cost / risk**: pushing logic into the storage layer; coverage limits.
|
|
64
|
+
- **Validate by**: rows/bytes after pushdown; pipeline timing.
|
|
65
|
+
|
|
66
|
+
### 6. Better join strategy
|
|
67
|
+
- **Borrowed from**: relational query execution (hash join, sort-merge join).
|
|
68
|
+
- **The idea**: replace a nested-loop join with a hash join (build a hash table on the smaller side, probe with the larger) or a sort-merge join when inputs are already sorted, for large joins done in app code.
|
|
69
|
+
- **Code signals**: `for a: for b: if a.key == b.key` — nested-loop join over big collections.
|
|
70
|
+
- **Speculative win**: O(n·m) → ~O(n+m) for hash; ~O(n log n + m log m) for sort-merge if sorting is needed, ~O(n+m) if already sorted.
|
|
71
|
+
- **Cost / risk**: build-side memory (hash); sort cost (sort-merge); both degrade with key skew.
|
|
72
|
+
- **Validate by**: benchmark across input sizes.
|
|
73
|
+
|
|
74
|
+
### 7. Partition pruning
|
|
75
|
+
- **Borrowed from**: partitioned tables, time-series databases.
|
|
76
|
+
- **The idea**: organize data into partitions (by time/key range) and skip whole partitions that can't match a query.
|
|
77
|
+
- **Code signals**: scanning all data when queries are bounded by time/range; growing scan cost over time.
|
|
78
|
+
- **Speculative win**: skips most of the dataset for bounded queries.
|
|
79
|
+
- **Cost / risk**: partition scheme must match query patterns; cross-partition queries.
|
|
80
|
+
- **Validate by**: partitions scanned per query; timing.
|
|
81
|
+
|
|
82
|
+
### 8. Compression / encoding (dictionary, RLE, delta)
|
|
83
|
+
- **Borrowed from**: columnar stores, time-series compression.
|
|
84
|
+
- **The idea**: encode data compactly (dictionary-encode repeated values, run-length for runs, delta for sequences) to shrink scans and fit more in cache/memory.
|
|
85
|
+
- **Code signals**: large low-cardinality or sequential datasets stored verbosely.
|
|
86
|
+
- **Speculative win**: less data to scan/transfer; sometimes operate directly on encoded form.
|
|
87
|
+
- **Cost / risk**: encode/decode CPU; random access into encoded blocks.
|
|
88
|
+
- **Validate by**: size reduction; scan benchmark on encoded data.
|
|
89
|
+
|
|
90
|
+
### 9. Append-only / sequential write design
|
|
91
|
+
- **Borrowed from**: write-ahead logs, event logs, journaling.
|
|
92
|
+
- **The idea**: write sequentially (append) and treat the log as the source of truth, deriving state from it — sequential I/O is far faster than random on both SSD and disk.
|
|
93
|
+
- **Code signals**: random in-place file/record updates; scattered small writes.
|
|
94
|
+
- **Speculative win**: sequential throughput; simpler durability/crash recovery.
|
|
95
|
+
- **Cost / risk**: compaction/snapshotting to bound size; read-path derivation.
|
|
96
|
+
- **Validate by**: write throughput; recovery correctness.
|
|
97
|
+
|
|
98
|
+
### 10. MVCC / snapshot reads instead of read locks
|
|
99
|
+
- **Borrowed from**: snapshot isolation (Postgres keeps new row versions in-table + VACUUM; InnoDB keeps the latest in-table and reconstructs old versions from undo logs).
|
|
100
|
+
- **The idea**: keep versioned data so readers see a consistent snapshot without blocking writers (and vice versa), removing reader-writer lock contention.
|
|
101
|
+
- **Code signals**: read locks blocking writers (or the reverse) on a hot store; lock contention between reads and writes.
|
|
102
|
+
- **Speculative win**: readers and writers might stop blocking each other if contention is genuinely reader-writer (not write-write).
|
|
103
|
+
- **Cost / risk**: version storage and garbage collection (Postgres bloat / InnoDB undo growth); complexity.
|
|
104
|
+
- **Validate by**: contention/off-CPU profiling; concurrency benchmark.
|
|
105
|
+
|
|
106
|
+
### 11. Precomputed / incremental aggregates (summary tables)
|
|
107
|
+
- **Borrowed from**: materialized aggregates, time-series rollups.
|
|
108
|
+
- **The idea**: maintain rollup/summary tables (counts, sums, histograms per bucket) updated incrementally on write, so aggregate queries read the summary instead of scanning raw rows.
|
|
109
|
+
- **Code signals**: expensive group-by/aggregate over raw rows per query.
|
|
110
|
+
- **Speculative win**: aggregate reads may become small lookups if queries align with the precomputed buckets.
|
|
111
|
+
- **Cost / risk**: summary maintenance on write; staleness if async.
|
|
112
|
+
- **Validate by**: aggregate query latency; rows scanned.
|
|
113
|
+
|
|
114
|
+
### 12. Zone maps / min-max indexes (data skipping)
|
|
115
|
+
- **Borrowed from**: ClickHouse skip indexes, Snowflake zone maps, Parquet row-group stats.
|
|
116
|
+
- **The idea**: store per-block min/max (and optionally null/count) metadata so a predicate can skip whole blocks whose range can't match — pruning before any data is read or decompressed. Cheap to maintain and most effective when data is sorted/clustered on the filtered column.
|
|
117
|
+
- **Code signals**: range/equality filters over large block-structured or chunked data; blocks read then fully discarded by a `WHERE`-style filter.
|
|
118
|
+
- **Speculative win**: skips most blocks for selective, range-correlated queries.
|
|
119
|
+
- **Cost / risk**: near-useless if values are randomly distributed across blocks (overlapping ranges); small metadata overhead.
|
|
120
|
+
- **Validate by**: blocks/row-groups pruned per query; bytes scanned before/after.
|
|
121
|
+
|
|
122
|
+
### 13. Late materialization (defer row reconstruction)
|
|
123
|
+
- **Borrowed from**: columnar OLAP execution (Vertica/C-Store lineage, DuckDB, Redshift).
|
|
124
|
+
- **The idea**: carry row IDs / position lists through filters and joins, fetching the non-predicate columns only for rows that survive — instead of assembling full rows up front (early materialization).
|
|
125
|
+
- **Code signals**: full objects/rows hydrated before filtering; expensive fields loaded for rows later discarded.
|
|
126
|
+
- **Speculative win**: avoids materializing columns for filtered-out rows; less memory and I/O on selective queries.
|
|
127
|
+
- **Cost / risk**: random-access tuple reconstruction can hurt for low-selectivity queries; more complex operators.
|
|
128
|
+
- **Validate by**: rows/columns materialized vs rows returned; query benchmark across selectivities.
|
|
129
|
+
|
|
130
|
+
### 14. Compressed bitmap index (roaring) for filtering
|
|
131
|
+
- **Borrowed from**: OLAP/search bitmap indexes (Druid, Pinot, Lucene; Roaring bitmaps).
|
|
132
|
+
- **The idea**: represent each value's matching row set as a compressed bitmap, then resolve multi-predicate filters with fast bitwise AND/OR/NOT over those bitmaps instead of scanning rows.
|
|
133
|
+
- **Code signals**: multi-attribute equality/set filters combined with AND/OR over a large dataset; repeated tag/category/flag filtering.
|
|
134
|
+
- **Speculative win**: filter resolution could reduce to bitwise set operations, cheaply intersecting many predicates, if cardinality is low-to-medium.
|
|
135
|
+
- **Cost / risk**: best for low-to-medium cardinality; index build/storage and update cost on writes.
|
|
136
|
+
- **Validate by**: rows scanned vs matched; multi-predicate filter benchmark.
|
|
137
|
+
|
|
138
|
+
### 15. Index intersection / multi-index AND (bitmap)
|
|
139
|
+
- **Borrowed from**: relational query optimizers — Postgres "Combining Multiple Indexes" (BitmapAnd / BitmapOr over per-index bitmaps), Oracle bitmap index AND.
|
|
140
|
+
- **The idea**: rather than one wide composite index per query shape, let the optimizer combine several single-column indexes at query time — scan each into an in-memory bitmap and AND/OR them — so a mix of queries touching different column subsets is served by a few reusable indexes instead of a combinatorial set of composites.
|
|
141
|
+
- **Code signals**: ad-hoc multi-column `WHERE` filters whose columns vary per query; many overlapping composite indexes maintained to cover every combination; one predicate indexed while another forces a residual scan.
|
|
142
|
+
- **Speculative win**: a multi-predicate filter might be answered by intersecting per-column bitmaps if each predicate is individually selective, avoiding both a full scan and a proliferation of composite indexes.
|
|
143
|
+
- **Cost / risk**: each extra index scan adds cost, so the planner may still prefer one composite or a single index; bitmap loses index ordering (an `ORDER BY` then needs a sort); maintaining several indexes costs write time.
|
|
144
|
+
- **Validate by**: query plan shows a BitmapAnd/intersection (not a full scan); rows scanned vs matched; benchmark vs a single composite index — profile via perf-design.
|