Skip to content

perf(dedup): eliminate per-pair array allocation in compareLocations - #296

Open
guyghost wants to merge 1 commit into
developfrom
guyghost-performance-improvements-033
Open

perf(dedup): eliminate per-pair array allocation in compareLocations#296
guyghost wants to merge 1 commit into
developfrom
guyghost-performance-improvements-033

Conversation

@guyghost

@guyghost guyghost commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Context

As part of a performance pass over the live scan hot path (scanner.tsbuildDeterministicMissionsdeduplicateMissionsDetailedscoreMission), I surveyed 10 candidate optimizations. This PR lands the highest-impact, lowest-effort one.

The win

compareLocations runs 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:

// before — allocates per pair
[...aTokens, ...bTokens].some((token) => REMOTE_LOCATION_TOKENS.has(token))

Replaced with a zero-allocation helper that iterates the tiny constant needles set with O(1) Set.has checks:

// after — zero allocations
hasAnyToken(aTokens, REMOTE_LOCATION_TOKENS) ||
hasAnyToken(bTokens, REMOTE_LOCATION_TOKENS)

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 in jaccardSimilarity).

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 — clean
  • pnpm --filter @pulse/extension lint — 0 errors (8 pre-existing warnings in an untouched file)
  • ✅ Pre-push ci:check gate 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:

  1. relevance.ts — profile Set rebuilt per mission. rawStackScore rebuilds a Set from the profile's keywords on every mission during scoring. Hoist the Set construction out of the per-mission loop.
  2. feed.svelte.ts — per-keystroke text re-join. recomputeFilteredMissions re-joins all mission text on every search keystroke. Memoize per-mission searchable text.
  3. relevance.tsnormalizeWeights recomputed per mission. The weights object is rebuilt for every mission; it only depends on the profile, not the mission.
  4. location-matching.tshasTokenMatch uses Array.includes. Linear scan over arrays that could be Sets for O(1) lookups in the location matcher.
  5. dedup.tscomputeMissionScore(existing) recomputed inside candidate loop. Re-score the kept mission once outside the inner loop instead of per pair.
  6. location-matching.ts — repeated tokenization in findMetroArea. The same location string is tokenized multiple times; tokenize once and reuse.
  7. db.ts — batch IndexedDB reads. Several scan-path reads that could be combined into a single cursor / getAllKeys traversal.
  8. semantic-scorer.ts — cache key construction. String concatenation for cache keys on every lookup; could precompute.
  9. scanner.ts — redundant Array.from / spread on results. A couple of intermediate array materializations before dedup that could be streamed.

Note: trackStage / createDefaultPipelineStages() / runPipeline() in pipeline.ts were intentionally excluded — per application-tracking.model.md, that code is not part of the live scan runtime (dead code), so optimizing it would be wasted effort.


🤖 Generated with Copilot


Open in Devin Review

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>
Copilot AI lite review requested due to automatic review settings August 12, 2026 07:13
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pulse Ready Ready Preview Aug 12, 2026 7:13am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
pulse-dashboard Skipped Skipped Aug 12, 2026 7:13am

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 compareLocations to use hasAnyToken instead 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants