perf(dedup): eliminate per-pair array allocation in compareLocations - #296
Open
guyghost wants to merge 1 commit into
Open
perf(dedup): eliminate per-pair array allocation in compareLocations#296guyghost wants to merge 1 commit into
guyghost wants to merge 1 commit into
Conversation
compareLocations ran on every candidate pair during deduplication and
allocated a merged `[...aTokens, ...bTokens]` array just to run a single
.some() membership check against the 3-entry REMOTE_LOCATION_TOKENS set.
Replace it with hasAnyToken(), which iterates the tiny constant needles
set and does O(1) Set.has checks against each token set individually —
zero allocations. This is consistent with the file's existing
allocation-avoidance pattern (intersectionSize, jaccardSimilarity).
Semantics are identical: the OR of (any remote token in A) or (any
remote token in B) is equivalent to (any remote token in A∪B).
Adds a regression test that exercises the remote-context fallback with
disjoint location token sets ('Paris' vs 'Teletravail').
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
There was a problem hiding this comment.
Pull request overview
This PR optimizes the deduplication hot path in the extension’s pure scoring core by removing a per-candidate-pair array allocation inside compareLocations, replacing it with an allocation-free helper that checks for “remote-context” tokens.
Changes:
- Introduces
hasAnyToken(tokens, needles)to perform zero-allocation membership checks across token sets. - Updates
compareLocationsto usehasAnyTokeninstead of spreading/merging token arrays. - Adds a unit regression test to ensure disjoint-location missions still dedupe when one location implies remote context.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| apps/extension/src/lib/core/scoring/dedup.ts | Replaces per-pair merged-array allocation in compareLocations with an allocation-free helper. |
| apps/extension/tests/unit/scoring/dedup.test.ts | Adds regression coverage for the remote-context fallback when location token sets are disjoint. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
As part of a performance pass over the live scan hot path (
scanner.ts→buildDeterministicMissions→deduplicateMissionsDetailed→scoreMission), I surveyed 10 candidate optimizations. This PR lands the highest-impact, lowest-effort one.The win
compareLocationsruns on every candidate pair during deduplication. It allocated a merged array every call just to do a single.some()membership check against a 3-entry constant set:Replaced with a zero-allocation helper that iterates the tiny constant
needlesset with O(1)Set.haschecks:Semantics are identical (OR of remote-token-in-A or remote-token-in-B ≡ remote-token-in-A∪B). This matches the file's existing allocation-avoidance style (
intersectionSize, inclusion-exclusion injaccardSimilarity).Verification
vitest run tests/unit/scoring/dedup.test.ts— 22/22 pass (added a regression test covering the remote-context fallback with disjoint location token sets)pnpm --filter @pulse/extension typecheck— cleanpnpm --filter @pulse/extension lint— 0 errors (8 pre-existing warnings in an untouched file)ci:checkgate passed (format + lint + typecheck + test + build)The other 9 improvements identified (not in this PR)
Listed for visibility / future work, roughly ordered by impact-to-effort:
relevance.ts— profile Set rebuilt per mission.rawStackScorerebuilds aSetfrom the profile's keywords on every mission during scoring. Hoist theSetconstruction out of the per-mission loop.feed.svelte.ts— per-keystroke text re-join.recomputeFilteredMissionsre-joins all mission text on every search keystroke. Memoize per-mission searchable text.relevance.ts—normalizeWeightsrecomputed per mission. The weights object is rebuilt for every mission; it only depends on the profile, not the mission.location-matching.ts—hasTokenMatchusesArray.includes. Linear scan over arrays that could beSets for O(1) lookups in the location matcher.dedup.ts—computeMissionScore(existing)recomputed inside candidate loop. Re-score the kept mission once outside the inner loop instead of per pair.location-matching.ts— repeated tokenization infindMetroArea. The same location string is tokenized multiple times; tokenize once and reuse.db.ts— batch IndexedDB reads. Several scan-path reads that could be combined into a single cursor /getAllKeystraversal.semantic-scorer.ts— cache key construction. String concatenation for cache keys on every lookup; could precompute.scanner.ts— redundantArray.from/ spread on results. A couple of intermediate array materializations before dedup that could be streamed.🤖 Generated with Copilot