mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
perf: avoid splitting every path during file autocomplete (#18919)
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { stripTypeScriptTypes } from 'node:module'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
|
||||
const baseline = process.argv[2]
|
||||
if (!baseline) {
|
||||
throw new Error('Usage: node config/scripts/mobile-file-ranking-benchmark.mjs <baseline-ref>')
|
||||
}
|
||||
async function load(source) {
|
||||
const js = stripTypeScriptTypes(source, { mode: 'transform' })
|
||||
return await import(`data:text/javascript;base64,${Buffer.from(js).toString('base64')}`)
|
||||
}
|
||||
function measure(fn, paths, query) {
|
||||
for (let warmup = 0; warmup < 10; warmup++) {
|
||||
fn(paths, query, 16)
|
||||
}
|
||||
const samples = []
|
||||
for (let i = 0; i < 9; i++) {
|
||||
const start = performance.now()
|
||||
fn(paths, query, 16)
|
||||
samples.push(performance.now() - start)
|
||||
}
|
||||
return samples.sort((a, b) => a - b)[4]
|
||||
}
|
||||
const results = []
|
||||
for (const [file, name] of [
|
||||
['src/main/runtime/runtime-mobile-file-path-search.ts', 'rankRuntimeMobileFilePaths'],
|
||||
['mobile/src/session/mobile-native-chat-autocomplete.ts', 'rankSuggestions']
|
||||
]) {
|
||||
const before = (
|
||||
await load(execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }))
|
||||
)[name]
|
||||
const after = (await load(readFileSync(file, 'utf8')))[name]
|
||||
for (const count of [100, 100000]) {
|
||||
const paths = Array.from(
|
||||
{ length: count },
|
||||
(_, i) => `src/components/workspace/group-${i % 100}/file-${i}.tsx`
|
||||
)
|
||||
for (const query of ['file-9', 'missing', 'workspace']) {
|
||||
assert.deepEqual(after(paths, query, 16), before(paths, query, 16))
|
||||
results.push({
|
||||
function: name,
|
||||
paths: count,
|
||||
query,
|
||||
beforeMs: measure(before, paths, query),
|
||||
afterMs: measure(after, paths, query)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify({ node: process.version, platform: process.platform, results }, null, 2))
|
||||
@@ -81,7 +81,7 @@ export function rankSuggestions(candidates: readonly string[], query: string, li
|
||||
const substring: string[] = []
|
||||
for (const candidate of candidates) {
|
||||
const lower = candidate.toLowerCase()
|
||||
const base = lower.split('/').pop() ?? lower
|
||||
const base = lower.slice(lower.lastIndexOf('/') + 1)
|
||||
if (lower.startsWith(q) || base.startsWith(q)) {
|
||||
prefix.push(candidate)
|
||||
} else if (lower.includes(q)) {
|
||||
|
||||
@@ -6,6 +6,37 @@ import {
|
||||
} from './runtime-mobile-file-path-search'
|
||||
|
||||
describe('rankRuntimeMobileFilePaths', () => {
|
||||
it('preserves basename matching, ordering and total counts for unusual paths', () => {
|
||||
const paths = [
|
||||
'',
|
||||
'/',
|
||||
'a/',
|
||||
'a//b.ts',
|
||||
'b.ts',
|
||||
'B.TS',
|
||||
'a\\b.ts',
|
||||
'界/😀.ts',
|
||||
'.hidden',
|
||||
'a/./b.ts'
|
||||
]
|
||||
for (const query of ['', ' ', 'b', '.ts', '😀', '/', 'a\\', 'missing']) {
|
||||
for (const limit of [0, 1, 3, 100]) {
|
||||
const q = query.trim().toLowerCase()
|
||||
const prefix = paths.filter((path) => {
|
||||
const lower = path.toLowerCase()
|
||||
return lower.startsWith(q) || (lower.split('/').pop() ?? lower).startsWith(q)
|
||||
})
|
||||
const other = paths.filter(
|
||||
(path) => !prefix.includes(path) && path.toLowerCase().includes(q)
|
||||
)
|
||||
expect(rankRuntimeMobileFilePaths(paths, query, limit)).toEqual({
|
||||
paths: [...prefix, ...other].slice(0, limit),
|
||||
totalCount: prefix.length + other.length
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('ranks path and basename prefixes before substrings and caps output', () => {
|
||||
expect(
|
||||
rankRuntimeMobileFilePaths(
|
||||
|
||||
@@ -76,7 +76,7 @@ export function rankRuntimeMobileFilePaths(
|
||||
let totalCount = 0
|
||||
for (const path of paths) {
|
||||
const lower = path.toLowerCase()
|
||||
const basename = lower.split('/').pop() ?? lower
|
||||
const basename = lower.slice(lower.lastIndexOf('/') + 1)
|
||||
if (lower.startsWith(normalizedQuery) || basename.startsWith(normalizedQuery)) {
|
||||
totalCount++
|
||||
if (prefix.length < limit) {
|
||||
|
||||
Reference in New Issue
Block a user