QVAC
Announcements7 min read

TurboVec: faster local search over far more documents

Searching your own documents with AI means comparing your question against every vector you have stored, and that gets slow and memory-hungry as the collection grows. TurboVec is a vector index that makes the search faster and the stored vectors much smaller, with no training step over your data. It is available in the QVAC SDK, and the RAG code you already write does not change.

Thomas Blanc
TurboVec: faster local search over far more documents

What is TurboVec

When you search your own documents with AI, the text is first turned into vectors: long lists of numbers that place similar meanings close together. A search compares your question's vector against the stored ones and returns the nearest. That works well until the collection grows, at which point holding every vector in memory and scanning them all becomes the slow part.

A vector index is what stops that from happening. It keeps vectors compressed so a full scan is smaller and cheaper, then the top hits can be compared more precisely.

This search is the first half of RAG, retrieval-augmented generation: find the passages that answer a question, then hand them to a model as context so it answers from your documents rather than from memory. The index decides how fast that first half runs and how much memory it takes.

TurboVec is one of those indexes, and its particular quality is that it compresses hard without needing to study your data first. The original is an open-source Rust project under MIT, built on TurboQuant, a compression algorithm from Google Research.

QVAC does not depend on that Rust code. We implemented the same TurboQuant-based quantizer natively in the fabric runtime, the forked llama.cpp and ggml layer the SDK's engines already run on, so the algorithm arrives with the SDK and there is no Rust in your dependency tree. We benchmarked ours against the 0.9.0 Rust release we reimplemented: search throughput is close, and the one place ours is slower today is the first ingestion of a collection, which a later version will close.

If the name rings a bell, TurboQuant is the Google Research algorithm QVAC already implements elsewhere in the SDK: there the compression is applied to the KV cache, the memory a model keeps while it generates text. The vector index applies it at the other end, to the vectors your question is compared against. One algorithm, both ends of the same pipeline.

The two act at opposite ends of the same pipeline.

One algorithm, two ends of the pipeline. TurboQuant compresses numbers to 4 bits with no training pass, and the QVAC SDK uses it twice.
One algorithm, two ends of the pipeline. TurboQuant compresses numbers to 4 bits with no training pass, and the QVAC SDK uses it twice.

TurboVec performance

Squeezing vectors down raises one question before any other: does the search still find the right thing? The paper answers it on 100,000 passages with 1,000 queries, scoring TurboVec against an uncompressed exact search over the same data. Those are the authors' measurements of their own implementation, so read them as the properties of the algorithm rather than as timings of the QVAC build.

It finds the right passage. Across those 1,000 queries the correct passage came back inside the top five results every single time, which is exactly what the uncompressed search managed. It came back as the top result 97% of the time, against 100% uncompressed. So what the compression costs is the ordering of the top few results rather than the answer itself, and for a search that feeds an LLM that is the difference that counts: the right passage still lands in the context.

The index is eight times smaller. Each number in a vector is kept in 4 bits rather than 32, so a vector of 1,536 numbers takes 768 bytes where it used to take 6,144. On the paper's 100,000 passages that is 73.2 MB against 585.9 MB, and ten million of those vectors come down from about 61 GB to about 8 GB. This is especially helpful for local AI, where memory is the constraint that decides what runs at all: a phone or a laptop is already sharing its RAM with the model doing the answering.

Query speed. The paper measures two setups:

  • A TurboVec index held in memory: 11 ms per query.
  • An existing deployment scanning a warehouse table: roughly 707 ms per query.

Most of that gain comes from the architecture rather than from the compression.

One last property matters on a personal device. An index that learns from your data ends up carrying a summary of it, and a summary can be interrogated. The paper got a membership inference attack to guess whether a record was in the collection 57.3% of the time against a learned index, and 50.0% against this one, which is a coin flip. That matters because the collection is somebody's own material: their files, their mail, their notes. Local AI exists so that material never has to leave the device, and an index that quietly encodes a summary of it would put part of it back on the table.

When to use TurboVec

These are the situations where TurboVec shines, and where the performance gain above is largest.

  • When the collection is large. Tens of thousands of chunks and up: a document archive, a mail history, a codebase, a knowledge base, years of notes. This is where an index earns its existence, and where eight times smaller decides whether the thing fits in memory.
  • When the collection keeps changing. Documents arriving daily, or a new user whose files look nothing like the last one's. An index that studied a sample of your data goes stale in exactly that situation, and this one has nothing to go stale.

Skip it when the collection is small. For a few hundred chunks, comparing every vector directly is fast, exact and simpler to reason about. Compression would cost you a little accuracy and buy you nothing.

How it works

Compressing a vector means storing each of its numbers with fewer bits, which comes down to rounding. Every method has to decide where to put the rounding points.

Product Quantization, the standard approach, works that out from your data: it samples your vectors, groups them, and keeps one representative per group as a reference list (the literature calls it a codebook). The list has to exist before anything can be stored, and it only describes data resembling the sample, so a new language or a new file type compresses worse and can mean building it again.

TurboVec never builds one. Before compressing, it rotates every vector the same way. The rotation is picked at random once, then reused for everything. Rotating keeps the distances between vectors intact, so the search still finds the same neighbours. What changes is the range the individual numbers fall in. After the rotation they spread out in the same predictable way, whatever the data looked like going in. Data that leaned heavily one way and data that sat in a tight clump come out looking much the same.

Knowing the spread in advance means the rounding points can be calculated rather than learned, and the training step goes with them. The paper calls this codebook-oblivious: the compression depends on no property of your data. So new vectors compress exactly like the first ones, and the index holds no summary of your documents.

How to use TurboVec with QVAC

One flag in qvac.config.json, set before the first SDK call:

{ "ragTurbovec": true }
npm install @qvac/[email protected]
const modelId = await loadModel({ modelSrc: EMBEDDINGGEMMA_300M_Q8_0 })
await ragIngest({ modelId, workspace: 'my-archive', documents })
const results = await ragSearch({ modelId, workspace: 'my-archive', query, topK: 5 })

ragIngest() and ragSearch() keep the same signatures and the same return shapes. What changes is the index underneath. Four things to know:

  • Opt-in. Upgrading to 0.19 on its own changes nothing.
  • CPU-only at this release. The embedding model still uses a GPU where there is one.
  • A workspace is pinned to the index it was created with. The flag does not migrate an existing workspace, so create a new one to compare.
  • Embedding dimensions must divide by 8 and stay at or below 1,024, on a 64-bit host. EmbeddingGemma 300M at 768 dimensions fits.

This is not a separate database you have to run. HyperDB stays the durable store for documents and mutations, the store the SDK's RAG already used, and TurboVec replaces the index on top of it. That is also why the log keeps a [rag:hyperdb] prefix.

To confirm which index a workspace got, read its marker: {"version":1,"adapterType":"turbovec"}.

Why this matters for local AI

Almost every constraint that makes local AI hard comes back to memory, or to work you cannot do without a server. TurboVec drops a training pass and shrinks the index eight times, so more of someone's own archive fits on hardware they already own, and none of it has to leave the machine to be searched.

Share

Latest articles

View all articles
Guides
Local AI 101: what local models are good for

Local AI 101: what local models are good for

Running AI on your own machine raises two questions at once: which models your laptop can actually run, and which model to use for which task. This first part answers the second one, job by job, across chat, documents, speech, translation, images and video. Every model listed runs through QVAC, the free and open-source toolkit we build for local AI, on laptops and on phones, with nothing leaving your device.

Read More

Stay updated

Never miss a release

New versions, breaking changes and migration notes - straight to your inbox. No spam, unsubscribe anytime.