From 6917058ce271f1f42685af4381772e67a9aefda5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 00:39:00 +0000 Subject: [PATCH] perf: reuse Uint8Array and fast O(K) reset for visited tracker in search engine Co-authored-by: AhmmedSamier <17784876+AhmmedSamier@users.noreply.github.com> --- language-server/apply_patch.sh | 91 +++++++++++++++++++ language-server/benchmarks/results_burst.json | 29 ++++++ language-server/src/core/search-engine.ts | 43 ++++++--- 3 files changed, 150 insertions(+), 13 deletions(-) create mode 100755 language-server/apply_patch.sh create mode 100644 language-server/benchmarks/results_burst.json diff --git a/language-server/apply_patch.sh b/language-server/apply_patch.sh new file mode 100755 index 00000000..144b0503 --- /dev/null +++ b/language-server/apply_patch.sh @@ -0,0 +1,91 @@ +#!/bin/bash +patch -p1 << 'PATCH' +--- a/src/core/search-engine.ts ++++ b/src/core/search-engine.ts +@@ -183,6 +183,9 @@ + topIndices: number[]; + } | null = null; + ++ private visitedBuffer: Uint8Array = new Uint8Array(0); ++ private visitedTracker: number[] = []; ++ + public itemsMap: Map = new Map(); + private readonly fileItemByNormalizedPath: Map = new Map(); + private activityWeight: number = 0.3; +@@ -1631,16 +1634,29 @@ + const searchContext = this.prepareSearchContext(query, scope); + // ⚡ Bolt: Fast integer ID tracking using Uint8Array instead of Set + const preferredIndices = this.getPreferredIndicesForQuery(scope, query, indices); +- const visited = preferredIndices.length > 0 ? new Uint8Array(this.items.length) : undefined; +- +- if (preferredIndices.length > 0) { +- this.searchWithIndices(preferredIndices, searchContext, heap, token, visited); ++ ++ const useVisited = preferredIndices.length > 0; ++ if (useVisited && this.visitedBuffer.length < this.items.length) { ++ this.visitedBuffer = new Uint8Array(this.items.length); + } + +- if (indices) { +- this.searchWithIndices(indices, searchContext, heap, token, visited); +- } else { +- this.searchAllItems(searchContext, heap, token, visited); ++ try { ++ if (preferredIndices.length > 0) { ++ this.searchWithIndices(preferredIndices, searchContext, heap, token, useVisited); ++ } ++ ++ if (indices) { ++ this.searchWithIndices(indices, searchContext, heap, token, useVisited); ++ } else { ++ this.searchAllItems(searchContext, heap, token, useVisited); ++ } ++ } finally { ++ if (useVisited) { ++ for (let i = 0; i < this.visitedTracker.length; i++) { ++ this.visitedBuffer[this.visitedTracker[i]] = 0; ++ } ++ this.visitedTracker.length = 0; ++ } + } + + const results = heap.getSorted(); +@@ -1776,16 +1792,17 @@ + context: ReturnType, + heap: MinHeap, + token?: CancellationToken, +- visited?: Uint8Array, ++ useVisited: boolean = false, + ): void { + for (let j = 0; j < indices.length; j++) { + if (j % 500 === 0 && token?.isCancellationRequested) break; + const i = indices[j]; +- if (visited) { +- if (visited[i] === 1) { ++ if (useVisited) { ++ if (this.visitedBuffer[i] === 1) { + continue; + } +- visited[i] = 1; ++ this.visitedBuffer[i] = 1; ++ this.visitedTracker.push(i); + } + this.processItemForSearch(i, context, heap); + } +@@ -1796,12 +1813,12 @@ + context: ReturnType, + heap: MinHeap, + token?: CancellationToken, +- visited?: Uint8Array, ++ useVisited: boolean = false, + ): void { + const count = context.items.length; + for (let i = 0; i < count; i++) { + if (i % 500 === 0 && token?.isCancellationRequested) break; +- if (visited && visited[i] === 1) { ++ if (useVisited && this.visitedBuffer[i] === 1) { + continue; + } + this.processItemForSearch(i, context, heap); + } +PATCH diff --git a/language-server/benchmarks/results_burst.json b/language-server/benchmarks/results_burst.json new file mode 100644 index 00000000..acfd56cc --- /dev/null +++ b/language-server/benchmarks/results_burst.json @@ -0,0 +1,29 @@ +[ + { + "name": "Burst Search \"App\" (Matches found)", + "avgMs": 0.022927290000000086, + "totalMs": 2.2927290000000085, + "minMs": 0.005182999999988169, + "maxMs": 0.22028199999999742, + "p95Ms": 0.07572100000004411, + "stdDevMs": 0.03326143676686062 + }, + { + "name": "Burst Search \"Zzz\" (No match)", + "avgMs": 25.31047841, + "totalMs": 2531.047841, + "minMs": 21.281904999999824, + "maxMs": 58.99728899999991, + "p95Ms": 50.09768800000029, + "stdDevMs": 7.7481483316893245 + }, + { + "name": "Burst Search \"S\" (Many matches)", + "avgMs": 0.6536615299999994, + "totalMs": 65.36615299999994, + "minMs": 0.5750079999997979, + "maxMs": 0.8889819999999418, + "p95Ms": 0.7484010000002854, + "stdDevMs": 0.05215705758937176 + } +] \ No newline at end of file diff --git a/language-server/src/core/search-engine.ts b/language-server/src/core/search-engine.ts index b81cdd20..d73317f3 100644 --- a/language-server/src/core/search-engine.ts +++ b/language-server/src/core/search-engine.ts @@ -183,6 +183,9 @@ export class SearchEngine implements ISearchProvider { topIndices: number[]; } | null = null; + private visitedBuffer: Uint8Array = new Uint8Array(0); + private visitedTracker: number[] = []; + public itemsMap: Map = new Map(); private readonly fileItemByNormalizedPath: Map = new Map(); private activityWeight: number = 0.3; @@ -1628,16 +1631,29 @@ export class SearchEngine implements ISearchProvider { const searchContext = this.prepareSearchContext(query, scope); // ⚡ Bolt: Fast integer ID tracking using Uint8Array instead of Set const preferredIndices = this.getPreferredIndicesForQuery(scope, query, indices); - const visited = preferredIndices.length > 0 ? new Uint8Array(this.items.length) : undefined; - if (preferredIndices.length > 0) { - this.searchWithIndices(preferredIndices, searchContext, heap, token, visited); + const useVisited = preferredIndices.length > 0; + if (useVisited && this.visitedBuffer.length < this.items.length) { + this.visitedBuffer = new Uint8Array(this.items.length); } - if (indices) { - this.searchWithIndices(indices, searchContext, heap, token, visited); - } else { - this.searchAllItems(searchContext, heap, token, visited); + try { + if (preferredIndices.length > 0) { + this.searchWithIndices(preferredIndices, searchContext, heap, token, useVisited); + } + + if (indices) { + this.searchWithIndices(indices, searchContext, heap, token, useVisited); + } else { + this.searchAllItems(searchContext, heap, token, useVisited); + } + } finally { + if (useVisited) { + for (let i = 0; i < this.visitedTracker.length; i++) { + this.visitedBuffer[this.visitedTracker[i]] = 0; + } + this.visitedTracker.length = 0; + } } const results = heap.getSorted(); @@ -1760,16 +1776,17 @@ export class SearchEngine implements ISearchProvider { context: ReturnType, heap: MinHeap, token?: CancellationToken, - visited?: Uint8Array, + useVisited: boolean = false, ): void { for (let j = 0; j < indices.length; j++) { if (j % 500 === 0 && token?.isCancellationRequested) break; const i = indices[j]; - if (visited) { - if (visited[i] === 1) { + if (useVisited) { + if (this.visitedBuffer[i] === 1) { continue; } - visited[i] = 1; + this.visitedBuffer[i] = 1; + this.visitedTracker.push(i); } this.processItemForSearch(i, context, heap); } @@ -1779,12 +1796,12 @@ export class SearchEngine implements ISearchProvider { context: ReturnType, heap: MinHeap, token?: CancellationToken, - visited?: Uint8Array, + useVisited: boolean = false, ): void { const count = context.items.length; for (let i = 0; i < count; i++) { if (i % 500 === 0 && token?.isCancellationRequested) break; - if (visited && visited[i] === 1) { + if (useVisited && this.visitedBuffer[i] === 1) { continue; } this.processItemForSearch(i, context, heap);