bench-search.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { Effect } from "effect"
  2. import { Fff } from "@opencode-ai/core/filesystem/fff.bun"
  3. import { AppRuntime } from "@/effect/app-runtime"
  4. import { Search } from "@opencode-ai/core/filesystem/search"
  5. import { InstanceStore } from "@/project/instance-store"
  6. const dir = process.cwd()
  7. const FILE_QUERIES = ["fff", "package.json", "tools/ experiment"]
  8. const GREP_QUERIES = ["FileFinder", "import", "grep", "autocomplete"]
  9. const GLOB_QUERIES = ["**/*.test.ts"]
  10. const FILE_LIMIT = 100
  11. const GREP_LIMIT = 50
  12. const GLOB_LIMIT = 50
  13. const run = <A>(effect: Effect.Effect<A, unknown, Search.Service>) =>
  14. AppRuntime.runPromise(
  15. InstanceStore.Service.use((store) => store.provide({ directory: dir }, effect as never)),
  16. ) as Promise<A>
  17. // --- raw Fff picker ---
  18. const t0 = performance.now()
  19. const made = Fff.create({ basePath: dir, aiMode: true })
  20. if (!made.ok) {
  21. console.error("Fff.create failed:", made.error)
  22. process.exit(1)
  23. }
  24. const picker = made.value
  25. console.log(`picker create: ${(performance.now() - t0).toFixed(1)}ms`)
  26. const tw = performance.now()
  27. await picker.waitForScan(2_500)
  28. console.log(`wait for scan: ${(performance.now() - tw).toFixed(1)}ms`)
  29. // warmup grep to let the content index build
  30. const tWarmup = performance.now()
  31. picker.grep("_warmup_", { mode: "regex", maxMatchesPerFile: 1, timeBudgetMs: 1_500 })
  32. console.log(`grep warmup: ${(performance.now() - tWarmup).toFixed(1)}ms`)
  33. console.log()
  34. console.log("--- raw picker (warm) ---")
  35. for (const q of FILE_QUERIES) {
  36. const t = performance.now()
  37. const r = picker.fileSearch(q, { pageSize: Math.max(FILE_LIMIT, 100) })
  38. const count = r.ok ? r.value.items.length : "err"
  39. console.log(`[picker] fileSearch "${q}": ${(performance.now() - t).toFixed(1)}ms (${count} results)`)
  40. }
  41. for (const q of GREP_QUERIES) {
  42. const t = performance.now()
  43. const r = picker.grep(q, { mode: "regex", pageSize: GREP_LIMIT, timeBudgetMs: 1_500 })
  44. const count = r.ok ? r.value.items.length : "err"
  45. console.log(`[picker] grep "${q}": ${(performance.now() - t).toFixed(1)}ms (${count} matches)`)
  46. }
  47. picker.destroy()
  48. // --- Ripgrep service (via Search with file:["."] to force rg path) ---
  49. console.log()
  50. console.log("--- Ripgrep (via Search service) ---")
  51. // warmup
  52. await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: "_warmup_rg_", limit: 1, file: ["."] })))
  53. for (const q of GREP_QUERIES) {
  54. const t = performance.now()
  55. const r = await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: q, limit: GREP_LIMIT, file: ["."] })))
  56. console.log(
  57. `[ripgrep] grep "${q}": ${(performance.now() - t).toFixed(1)}ms (${r.items.length} total, limit is per-file not total)`,
  58. )
  59. }
  60. // --- Search service: init breakdown ---
  61. console.log()
  62. // 1) runtime + InstanceState + picker create + scan poll
  63. const tRuntime = performance.now()
  64. await run(Search.Service.use((svc) => svc.file({ cwd: dir, query: "_warmup_file_", limit: 1 })))
  65. console.log(`[Search] init file (runtime + picker + scan): ${(performance.now() - tRuntime).toFixed(1)}ms`)
  66. // 2) grep warmup (content index cold-start inside the Search service picker)
  67. const tGrepWarmup = performance.now()
  68. await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: "_warmup_grep_", limit: 1 })))
  69. console.log(`[Search] init grep (content index warmup): ${(performance.now() - tGrepWarmup).toFixed(1)}ms`)
  70. console.log()
  71. console.log("--- Search service (warm) ---")
  72. for (const q of FILE_QUERIES) {
  73. const t = performance.now()
  74. const r = await run(Search.Service.use((svc) => svc.file({ cwd: dir, query: q, limit: FILE_LIMIT })))
  75. console.log(
  76. `[Search.file] "${q}": ${(performance.now() - t).toFixed(1)}ms (${r?.length ?? "undefined (cache fallback)"} results)`,
  77. )
  78. }
  79. for (const q of GREP_QUERIES) {
  80. const t = performance.now()
  81. const r = await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: q, limit: GREP_LIMIT })))
  82. console.log(
  83. `[Search.search] "${q}": ${(performance.now() - t).toFixed(1)}ms (${r.items.length} matches, engine=${r.engine})`,
  84. )
  85. }
  86. for (const q of GLOB_QUERIES) {
  87. const t = performance.now()
  88. const r = await run(Search.Service.use((svc) => svc.glob({ cwd: dir, pattern: q, limit: GLOB_LIMIT })))
  89. console.log(
  90. `[Search.glob] "${q}": ${(performance.now() - t).toFixed(1)}ms (${r.files.length} files, truncated=${r.truncated})`,
  91. )
  92. }
  93. process.exit(0)