Three Ways to Make HyperCrux Faster: A C Search Loop, AltSql DB Underneath or a New Engine in C
HyperCrux 0.1 is a Go library and command on top of SQLite, and it’s quick at the sizes it was built for. In the recorded benchmarks on a two-core cloud machine, a get by key takes 18 microseconds and a walk one link out 43 microseconds. A search among 100,000 vectors of 384 values takes 0.41 seconds. The question that comes up next is how much faster it could get, and what each step would cost.
There are three paths, from a small one that keeps everything to a large one that builds a new database. None of them is built yet. This post sets them side by side, and the largest one now has a written plan.

Where the Time Goes Today
The benchmarks already say a lot about this. A search among 10,000 vectors of 384 values takes 42 milliseconds, about 4.2 microseconds a vector. A search among 100,000 vectors where only a tenth pass the filter takes 0.13 seconds. It compares as many vectors as the 42-millisecond search, so the extra 86 milliseconds go on the 90,000 rows that fail, about a microsecond each. That’s SQLite reading a row and testing it. By that measure, roughly three quarters of each comparison happens after SQLite hands the row over: the driver copies the vector into Go, and a Go loop decodes the floats one at a time.
Longer vectors cost more per value. Among 10,000 vectors of 1,536 values a search takes 0.12 seconds, so each extra value adds about 7 nanoseconds. The loop is only part of that: rows this long spill onto a second SQLite page, and each vector is copied into Go before the loop reads it.
One more figure matters for what follows. A committed write takes 0.35 milliseconds, and most of that, by our reading, is the disk confirming the data is safe. No rewrite removes that without weakening the guarantee. Gets and walks are fast already. A get is one indexed SQL query run through Go’s database/sql, a walk is a recursive SQL query, and neither has been timed in parts.
Path One: A C Search Loop Inside SQLite
The smallest step moves only the hot loop. A short C extension runs inside SQLite and compares each vector there, using SIMD instructions that work on several values at once, in the same float64 arithmetic HyperCrux uses now. The copy into Go goes away for every row, and so does most of the cost of the loop. The same code makes SQL’s distance() far cheaper, since today every call crosses from SQLite into Go and back.
Everything else stays: the SQLite file, the rules stored in it as triggers, the Go API, the command and every test. HyperCrux already builds SQLite from C, so no new tools are needed. The cost is some C in a codebase that’s meant to need little attention, which is why this step waits until search speed actually holds someone back.
Path Two: AltSql DB Underneath
AltSql is a small database for sensor fleets, written in C. Its gateway database, AltSql DB, keeps records in a copy-on-write B-tree with a direct key path that never touches SQL, and it comes with an unusually thorough crash-test harness. Swapping it in under HyperCrux’s Go layer would make gets faster and the HyperCrux code leaner, since the triggers and the schema bookkeeping around them would go. Files could shrink as well, because each key is stored three times today: in its row, in the row’s primary-key index and in the key registry.
It would also cost HyperCrux what makes it worth having. The file would stop being an SQLite file, so a Python script that writes a HyperCrux file today by FORMAT.md’s rules couldn’t open it at all. AltSql DB’s SQL has no joins yet, and the one-statement query across all four handles needs one. It’s also an alpha that one process uses at a time. On its own this path gives up too much. Its storage and its direct key path are worth carrying into path three, though.
Path Three: A New Engine in C
The largest step, and the one aimed at the most speed, is a new database engine written in C with all four handles built in. Each table’s vectors would sit packed together, so a search reads them in one pass with no SQL row in between: 100,000 vectors of 384 values is 154 MB of numbers. Links would become rows of integers kept in two orders, one for each direction, so a walk reads short ranges of them and runs no recursive query. Keys would go through a direct path with no SQL on the way. Readers in other processes wouldn’t wait for the writer, and the plan aims for one sync per commit, kept only if it passes AltSql’s crash tests.
Under all of it would sit AltSql DB’s storage, extended with what HyperCrux needs and AltSql lacks today, such as readers across processes and page checksums. Search stays exact by default. An optional quick pass reads small 8-bit copies of the vectors first and checks against the full vectors only the candidates that could still win, so the answer matches a full scan’s.
The price is a new database. It means a long stretch of storage and crash-safety work before anyone should trust it with data, and even then it wouldn’t have SQLite’s decades of use behind it. SQLite’s tools would be left behind for good, with import and export as the bridge. The Beta plan sets out the design handle by handle, what to take from AltSql and what to add, targets set against 0.1’s recorded numbers, phases that each end at a gate, and the decisions to make before any code is written.
What Happens Next
Nothing changes for 0.1. It stays on SQLite and keeps getting fixes. Path one is the natural next step whenever search speed becomes the limit, and its SIMD code could carry into path three if that ever starts. Whatever gets built, the numbers on this site will keep coming from recorded test runs, and the download page has 0.1 in the meantime.