perf: replace map[string]bool sets with map[string]struct{} across hot paths - #679
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files
☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
🟢 Merge Queue — picked up This PR is in the queue and will be batched with other Next: No action needed — you'll get another comment when CI starts on the batch. View merge queue run. |
|
This PR could not be merged into the batch branch without conflicts with Next: Rebase onto or merge |
…y returns
Five hot-path locations used map[string]bool as a visited/dedup set,
storing only true values and paying one wasted byte per entry. Per the
high-performance Go guidelines (docs/development/high-performance-go.md),
pure sets must use map[string]struct{} — zero-byte value type.
- catalog/rule.go: scanIncludesForTarget + scanIncludesForTargetAbs
visited sets (called per-file per catalog directive, hot check path)
- lint/runcache.go: Invalidate/invalidate/invalidateDependents cycle guard
(called on every LSP file edit)
- config/kind_extends.go + validate.go: validateKindExtends, extendsChainSchemas,
KindExtendsChain visited sets; KindExtendsChain also fixed to return nil
instead of []string{} for empty results (project return-nil convention)
- config/merge.go + provenance.go: EffectiveKinds, resolveEffectiveKinds,
resolveKindsWithSources dedup sets (called per-file during engine run)
- pkg/mdsmith/workspace.go: memFS.dirEntries dedup set (workspace walk)
- lint/files.go: ResolveFilesWithOpts dedup set (CLI file resolution)
Red: TestKindExtendsChain_EmptyNameReturnsNil (wrong return type) +
type-mismatch compile errors in catalog tests (wrong visited type).
Green: all tests pass after signature + call-site updates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjbjBieiWMBKURdKVNnPvy
…check The previous commit changed `if name != "" && !seen[name]` to `if _, ok := seen[name]; name != "" && !ok`. In Go's `if init; cond` form the init statement always executes before the condition, so the map probe ran unconditionally — even when `name == ""` — reversing the original short-circuit semantics. Restore the guard-first order by nesting the membership check inside an explicit `if name != ""` block, matching the original evaluation order and preventing any wasted probe on empty segment paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QjbjBieiWMBKURdKVNnPvy
Third code-review pass found three issues:
1. validate.go and kind_extends.go: `chain := []string{}` allocates an
empty backing array on every call. `var chain []string` (nil slice)
defers the first allocation to the initial append, saving one heap
allocation on every call where the loop never appends (kinds without
an extends: chain in validateKindExtends, or when the extends chain
has no schemas in extendsChainSchemas).
2. provenance.go: `allRuleNames` was the last `map[string]bool` presence
set in the file not converted by the previous commit. Changed to
`map[string]struct{}` for consistency with the surrounding code.
3. workspace_test.go: Add TestMemFSDirEntriesIgnoresEmptySegment to
document and pin the empty-name-segment guard in memFS.dirEntries.
A key with a double-slash segment (e.g. "a//b.md") produces an empty
first component after stripping the directory prefix; the nested-if
guard introduced in 9b0fa79 must check `name != ""` before the
seen-map probe. The test constructs memFS directly (bypassing
NewMemWorkspace path.Clean) to exercise this path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjbjBieiWMBKURdKVNnPvy
ef818c0 to
6d89652
Compare
|
🟢 Merge Queue — picked up This PR is in the queue and will be batched with other Next: No action needed — you'll get another comment when CI starts on the batch. View merge queue run. |
|
🔵 Merge Queue — CI running Merged into batch branch Next: No action needed — you'll be notified when CI completes. |
|
✅ Merge Queue — merged This PR landed on Next: Done — nothing more to do here. |
Summary
Performance audit against docs/development/high-performance-go.md, which states: "map[K]struct{} for sets — zero-byte value type." Five hot-path locations and two related cleanup sites used
map[string]boolas presence-only sets, paying one wasted byte per entry and one wastedboolcomparison per read.Fixes (5 hot-path locations + 2 follow-ons)
1.
internal/rules/catalog/rule.go— include-graph traversal (hottest path)fileIncludesTarget/fileIncludesTargetAbsand their recursive workersscanIncludesForTarget/scanIncludesForTargetAbscarriedvisited map[string]boolthrough every per-file catalog directive evaluation. Changed tomap[string]struct{}. DFSdelete(visited, resolved)backtracking preserved.2.
internal/lint/runcache.go— LSP invalidation cycle guardInvalidate→invalidate→invalidateDependentspassed amap[string]boolcycle guard on every LSP file-edit event. Changed tomap[string]struct{}.3.
internal/config/kind_extends.go+validate.go— extends chain walkersThree chain-walking functions (
KindExtendsChain,extendsChainSchemas,validateKindExtends) usedmap[string]boolfor cycle detection. Changed tomap[string]struct{}.KindExtendsChainalso fixed to returnnilinstead of[]string{}on empty result (project nil-not-empty convention). Two intermediatechain := []string{}accumulators changed tovar chain []string(defers first allocation to first append).4.
internal/config/merge.go+provenance.go— per-file kind resolutionEffectiveKinds,resolveEffectiveKinds,resolveKindsWithSources, andallRuleNamesde-dup sets changed frommap[string]booltomap[string]struct{}.5.
internal/lint/files.go— CLI file deduplicationResolveFilesWithOptsseen-set changed tomap[string]struct{}.6.
pkg/mdsmith/workspace.go— memFS directory enumerationmemFS.dirEntriesseen-set changed. Also fixed a short-circuit evaluation bug in the conversion: theif init; condformif _, ok := seen[name]; name != "" && !okran the map probe before the guard (init always executes in Go'sif init; condform). Restored guard-first order with nestedifblocks.TDD
TestKindExtendsChain_EmptyNameReturnsNil(fails against old[]string{}return; passes aftervar out []string)map[string]struct{}before updating function signatures produces type-mismatch compile errors; fixing signatures restores greenTestMemFSDirEntriesIgnoresEmptySegmentadded to document and pin the empty-name-segment guard indirEntriesCode review passes
Three
--maxcode review passes were run:workspace.go) → fixed in9b0fa79chain := []string{}allocation (confirmed),allRuleNamesnot converted (plausible), missing workspace test (confirmed TDD gap) → fixed inef818c0Test plan
go test ./...— all packages greengo build ./...— clean buildinternal/integration/alloc_budget_test.gopasses (allocation budget gate)go run ./cmd/mdsmith check .to confirm markdown lints clean🤖 Generated with Claude Code
https://claude.ai/code/session_01QjbjBieiWMBKURdKVNnPvy
Generated by Claude Code