Launch offerPro with unlimited Projects for $19 — normally $29.Pro $19 — normally $29
← Blog

"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"

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, 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:

BeforeAfter
Matcher setup, first lens18.1 s, app frozen5.9 s, in the background
Match, 20 to 45 lines of text1.9 to 3.8 s, app frozen0.14 to 0.73 s, in the background
Peak memory1.5 GB1.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.