---
title: "\"Making Code Lens fast on a 3,000-file repo: 18 s to 5.9 s setup, 4 s to under 0.8 s per match\""
description: "On a large repository the first Code Lens in Bigshot froze the app for about 22 seconds. Why it happened, the four changes in 1.7.6 that fixed it, the numbers before and after, and what is still slow."
url: https://www.bigshot.pics/blog/code-lens-large-repos
---
# "Making Code Lens fast on a 3,000-file repo: 18 s to 5.9 s setup, 4 s to under 0.8 s per match"

*2026-10-22*


[Code Lens](https://www.bigshot.pics/docs/code-lens) matches the text you select on screen against
a map of your Project's code, then labels each component with its file and
line. On the small apps in our [benchmarks](https://www.bigshot.pics/blog/code-lens-benchmarks), a
match takes about 220 ms. On a 2,986-file repository, 22 MB of source with
363 page trees, the first Lens or X-ray froze Bigshot for about 22 seconds,
and every one after that for 2 to 4 seconds. The pointer turned into the
spinning wheel and the app stopped drawing.

Bigshot 1.7.6 fixes it. These are the numbers on that repository, before
and after:

| | Before | After |
|---|---|---|
| Matcher setup, first lens | 18.1 s, app frozen | 5.9 s, in the background |
| Match, 20 to 45 lines of text | 1.9 to 3.8 s, app frozen | 0.14 to 0.73 s, in the background |
| Peak memory | 1.5 GB | 1.2 GB |

The matches are the same. Results were byte-identical on six sets of
screen lines, and the Lens corpus of 20 stacks still scores 482 of 587
lines with no false positives.

## Why it froze

The Code map is a SQLite index on your Mac. Before a match, Bigshot loads
it into a matcher: every string in the code that could appear on screen,
split into normalised segments, with where each one is used. Then it looks
for the longest runs of screen text that match a segment.

Both steps ran on the main thread. The service that owns the Code map was
a main-actor class, so loading the matcher and matching a selection ran
where the app draws its windows. Indexing rebuilt the matcher on the main
thread too. On a 600-file repo setup takes about 3 seconds and a match
about 50 ms, which went unnoticed. On five times the files, with many more
translation keys and data strings, it didn't.

## What changed

1. **Load and match in the background.** The matcher is built and queried
   off the main thread. The app keeps drawing, the lens shows its loading
   card, and if you change the selection before a result lands, the old
   result is dropped.
2. **Warm the matcher early.** It starts loading when a lens opens, while
   you are still dragging out the selection, and again right after the
   Code map is indexed. Most of the 5.9 s now passes before you let go.
3. **Build segments on every core.** Normalising the strings was a single
   loop. It now runs in parallel.
4. **Find short runs through a word index.** The longest-match step
   scanned every segment for each line of screen text. A word index now
   finds the candidate segments directly, which took a match from
   seconds to under a second.

## What is still slow

5.9 seconds is still a long setup, and 1.2 GB is a lot of memory for a
matcher. We don't load it when Bigshot starts, because most launches never
open a lens and that memory would be held for nothing. The next step is to
store the normalised segments in the index itself, so setup becomes a
plain load from disk and memory drops with it.

If Code Lens is slow on your repository, update to 1.7.6.
