Vectordatabase draaien op een VPS: de beste aanpak
Bespaar op managed diensten door uw vectordatabase zelf te hosten. Vergelijk pgvector, Qdrant en Chroma. Leer hoe u RAM berekent voor uw index en voorkom latency op uw VPS.
Wat een vectordatabase op een VPS u daadwerkelijk kost
Het draaien van een vectordatabase op een VPS (virtual private server) elimineert het probleem waarvoor elke managed vendor een oplossing verkoopt. Uw applicatie en uw index bevinden zich op dezelfde machine, waardoor een zoekopdracht via een loopback-socket verloopt in plaats van via een netwerk. Wat overblijft zijn de kosten die altijd al de werkelijke kosten waren: het omzetten van tekst naar vectoren. Daarachter liggen nog twee factoren: de tijd die nodig is om de index op te bouwen en het RAM-geheugen dat de index in beslag neemt zolang deze actief is.
Dit verandert welke beslissingen van belang zijn. De regio en de round-trip-tijd naar een endpoint zijn niet langer uw zorg. Het product van het aantal vectoren, de dimensies en vier bytes wordt wel uw zorg, omdat dit bepaalt of de index past in het geheugen dat u maandelijks huurt.
Waar de milliseconden op één server daadwerkelijk blijven
Volg een gelijkenisquery door een zelfgehoste stack.
- De querytekst wordt door een embedding-model omgezet in een vector. Op een CPU duurt dit tientallen tot honderden milliseconden voor een korte string. Op een GPU zijn dit enkele milliseconden.
- De vector wordt naar de opslag gestuurd. Via loopback TCP of een Unix domain socket is dit een fractie van een milliseconde.
- De opslag doorloopt de index en retourneert de meest relevante rijen.
- Uw code leest de overeenkomende tekst en stelt een prompt samen.
Stap 1 is meestal de grootste factor in deze lijst. Stap 2 is de stap waarop gehoste aanbieders concurreren, maar op één server is deze verwaarloosbaar. Gok niet naar de verdeling. Meet beide kanten op uw eigen server.
curl http://127.0.0.1:11434/api/embed -s -o /dev/null \
-w 'embed: %{time_total}s\n' \
-d '{"model": "nomic-embed-text", "input": "how do I rotate the api key"}'Voer vervolgens \timing on uit in psql vóór de zoekopdracht. Als het eerste commando embed: 0.184312s afdrukt en psql antwoordt met Time: 4.201 ms, dan is het tunen van de index niet de juiste taak: uw latentie wordt veroorzaakt door het embedding-model. Het lokaal draaien van het embedding-model met Ollama plaatst stap 1 op dezelfde CPU als stap 2 en 3, waardoor beide helften strijden om dezelfde cores en hetzelfde RAM-geheugen. De ingest- en retrieval-loop rondom deze opslag wordt behandeld in de handleiding voor de zelfgehoste RAG-pipeline. RAG staat voor retrieval augmented generation: u doorzoekt uw eigen documenten en plakt de beste resultaten in een prompt.
Scan bij minder dan honderdduizend vectoren de volledige set
Een volledige scan vergelijkt de query met elke opgeslagen vector. De recall is per definitie perfect. Er is geen index of opbouwfase nodig en de data kan nooit verouderd raken ten opzichte van de bron.
De rekenkracht bepaalt wanneer deze methode niet meer voldoet. Een scan leest n * d * 4 bytes per query, waarbij n het aantal vectoren is en d de dimensie. Bij 100.000 vectoren van 768 dimensies is dat 307 MB per query, wat een moderne CPU in enkele tientallen milliseconden verwerkt. Bij 5 miljoen vectoren is dit 15 GB per query; dat is niet langer een bruikbare query.
Sla de vectoren daarom op in SQLite en voer de vergelijking uit in NumPy.
import sqlite3, numpy as np
db = sqlite3.connect("docs.db")
db.execute("CREATE TABLE IF NOT EXISTS docs (id INTEGER PRIMARY KEY, body TEXT, vec BLOB)")
def add(body, vec):
v = np.asarray(vec, dtype=np.float32)
v /= np.linalg.norm(v)
db.execute("INSERT INTO docs (body, vec) VALUES (?, ?)", (body, v.tobytes()))
db.commit()
def search(query_vec, k=5):
rows = db.execute("SELECT id, body, vec FROM docs").fetchall()
mat = np.frombuffer(b"".join(r[2] for r in rows), dtype=np.float32).reshape(len(rows), -1)
q = np.asarray(query_vec, dtype=np.float32)
q /= np.linalg.norm(q)
scores = mat @ q
return [(rows[i][0], rows[i][1], float(scores[i])) for i in np.argsort(-scores)[:k]]Beide zijden worden geschaald naar een eenheidslengte, waardoor het inwendig product gelijk is aan de cosinus-gelijkenis en een hogere score een nauwere match betekent. Laad mat eenmalig bij het opstarten in plaats van per query, zodat de SQLite-leesactie volledig buiten het kritieke pad valt.
Test de snelheid op uw eigen systeem voordat u deze methode verwerpt.
import time
t = time.perf_counter()
search(q)
print(f"{(time.perf_counter() - t) * 1000:.1f} ms")De reële beperkingen: dit is één proces dat de volledige matrix in het RAM-geheugen houdt. Het biedt geen metadata-filtering en geen ondersteuning voor gelijktijdige schrijfacties. Wanneer een van deze punten de reden is voor uw ontevredenheid, is het tijd om over te stappen. SQLite zelf is een serieuze server-side opslagoplossing, wat wordt behandeld in de gids over SQLite in productie, en als uw werkelijke workload bestaat uit het scannen van kolommen in plaats van het serveren van rijen, dan is de vergelijking tussen DuckDB en SQLite de relevantere leesstof.
pgvector, als u al Postgres gebruikt
Als uw applicatie al een Postgres-database gebruikt, voegt pgvector het minste nieuwe oppervlak toe. Het is een extensie, geen service. Vectoren bevinden zich in een normale tabel naast de rij die ze beschrijven, waardoor een gefilterde zoekopdracht een WHERE-clausule is in plaats van een tweede systeem dat synchroon moet blijven.
Ubuntu 24.04 bevat dit in de universe-component.
sudo apt update
sudo apt install -y postgresql-16-pgvector
sudo -u postgres psql -d yourdb -c 'CREATE EXTENSION vector;'Dat pakket is pgvector 0.6.0 per augustus 2026, wat ver achterloopt op de upstream-versie. Vooral voor iteratieve indexscans is versie 0.8 vereist, dus haal deze uit de eigen repository van het PostgreSQL-project.
sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-17-pgvectorVervang 17 door de major-versie van uw server, die sudo -u postgres psql -tAc 'SHOW server_version' weergeeft. Installeer het extensiepakket dat voor de verkeerde major-versie is gebouwd en CREATE EXTENSION faalt, omdat Postgres alleen in de share-directory van de actieve versie zoekt:
ERROR: could not open extension control file "/usr/share/postgresql/16/extension/vector.control": No such file or directoryHet schema is standaard SQL met één nieuw type.
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
doc_id bigint NOT NULL,
body text NOT NULL,
embedding vector(768)
);
SELECT id, body FROM chunks
ORDER BY embedding <=> '[0.013, -0.021, 0.004]'
LIMIT 5;<=> is cosinus-afstand, <-> is L2 (Euclidische) afstand en <#> is het negatieve inwendig product. Gebruik degene waarvoor uw embedding-model is getraind. Kiest u de verkeerde, dan treden er geen fouten op, maar zijn uw resultaten simpelweg stilletjes minder nauwkeurig.
Zonder index is die query een exacte zoekopdracht over elke rij; dit is de Postgres-versie van de brute force-methode hierboven en heeft dezelfde perfecte recall. Het verhogen van max_parallel_workers_per_gather zet meer cores in voor deze taak. Doe dit eerst en voeg pas later een index toe, omdat u nu een recall-baseline heeft om de index aan te toetsen.
Qdrant, wanneer de index groter wordt dan de database
Qdrant is een gespecialiseerde vector-store geschreven in Rust. Het is een geschikte keuze wanneer de index zo groot is dat u niet wilt dat het opbouwen ervan concurreert met uw Postgres-database, of wanneer u behoefte heeft aan payload-filtering en kwantisatie die pgvector niet biedt.
docker run -d --name qdrant \
-p 127.0.0.1:6333:6333 -p 127.0.0.1:6334:6334 \
-e QDRANT__SERVICE__API_KEY="$(openssl rand -hex 32)" \
-v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
qdrant/qdrantPoort 6333 bedient de REST (representational state transfer) API en een dashboard op /dashboard, en 6334 bedient gRPC. Twee details zijn van belang op een publieke VPS. De documentatie van Qdrant stelt dat de service standaard draait "zonder encryptie of authenticatie", en de -p 6333:6333 uit de quickstart bindt aan elke interface. Docker publiceert dit voorbij een ufw-regel omdat het zijn eigen forwarding-regels schrijft. Bind aan 127.0.0.1 en stel een API-sleutel in. Een Qdrant-instantie die bereikbaar is op een publiek IP-adres zonder sleutel, stelt uw documenten publiekelijk beschikbaar.
curl -s http://127.0.0.1:6333/collections -H "api-key: $QDRANT_API_KEY"Een gezond antwoord ziet eruit als {"result":{"collections":[]},"status":"ok","time":0.00002}. Het ontvangen van {"status":{"error":"Unauthorized"}} betekent dat de headernaam of de sleutel onjuist is, en het ontvangen van helemaal niets betekent dat de container niet draait of ergens anders aan is gebonden. Of die container de juiste vorm heeft voor uw server is de gebruikelijke vraag bij stateful-services, dus de vergelijking tussen Docker en een database op de host is hier ongewijzigd van toepassing.
Chroma en het doel ervan
Chroma is de kortste weg van niets naar een werkende retrieval-demo.
pip install chromadb
chroma run --path /srv/chromaDeze draait op poort 8000 en chromadb.HttpClient(host="localhost", port=8000) maakt hiermee verbinding. Chroma wordt geleverd met een standaard embedding-functie, waardoor een eerste prototype helemaal geen aparte modelserver nodig heeft.
Wees eerlijk over de afweging. Chroma is prettig omdat het de beslissingen waar deze handleiding over gaat verbergt: welke afstandsmetriek wordt gebruikt en hoeveel RAM het resultaat in beslag neemt. Dat is correct voor een prototype, maar onjuist voor de systemen waarvoor u wordt opgeroepen bij incidenten. Als uw data al in Postgres staat, voegt het verplaatsen naar Chroma een extra proces en een synchronisatieprobleem toe om een probleem op te lossen dat pgvector niet heeft.
Hoeveel RAM heeft de index nodig
Begin bij de ruwe vectoren, aangezien dit de ondergrens is en geen enkele optimalisatie deze kan verlagen.
bytes = number_of_vectors * dimensions * 4Vier bytes staat gelijk aan één 32-bit float per dimensie. De documentatie voor capaciteitsplanning van Qdrant hanteert een vermenigvuldigingsfactor van 1,5 voor metadata en de tijdelijke segmenten die tijdens optimalisatie worden aangemaakt:
memory_size = number_of_vectors * vector_dimension * 4 bytes * 1.5Hieronder staat die formule toegepast op één miljoen vectoren, met de dimensies die gangbare embedding-modellen produceren.
The data behind this chart
[
{
"label": "384 dims",
"raw_gib": 1.43,
"with_overhead_gib": 2.15
},
{
"label": "768 dims",
"raw_gib": 2.86,
"with_overhead_gib": 4.29
},
{
"label": "1024 dims",
"raw_gib": 3.81,
"with_overhead_gib": 5.72
},
{
"label": "1536 dims",
"raw_gib": 5.72,
"with_overhead_gib": 8.58
},
{
"label": "3072 dims",
"raw_gib": 11.44,
"with_overhead_gib": 17.17
}
]Dit zijn de uitkomsten van de formule in gibibytes (GiB), geen meting. Beschouw deze waarden als de hoeveelheid geheugenruimte die u moet reserveren. Een model met 768 dimensies, zoals nomic-embed-text, vereist voor een miljoen chunks ongeveer 4.29 GiB. Dit past binnen een 8 GB-plan, met ruimte over voor Postgres. Dezelfde dataset met 3072 dimensies vereist 17.17 GiB en past hier niet in.
De belangrijkste knop om aan te draaien is de eerste kolom van die tabel, niet de laatste. Het halveren van het aantal dimensies halveert voor altijd elk daaropvolgend byteverbruik. Een model met 768 dimensies dat iets lager scoort op een publieke ranglijst, is op een VPS vaak de betere technische keuze. Het halfvec-type van pgvector slaat vervolgens 16-bit floats op, wat het aantal bytes nogmaals halveert, en indexeert via een expressie:
CREATE INDEX ON chunks USING hnsw ((embedding::halfvec(768)) halfvec_cosine_ops);Eén limiet om rekening mee te houden voordat u een model kiest. Het vector-type van pgvector accepteert tot 16.000 dimensies, maar de HNSW- en IVFFlat-indexen ondersteunen er slechts 2.000. Daarboven indexeert u een halfvec-cast, die tot 4.000 gaat, of u indexeert helemaal niet.
Wat kosten m en ef_construction tijdens de opbouwfase
HNSW (hierarchical navigable small world) is het index-type waar zowel pgvector als Qdrant gebruik van maken. Het is een gelaagde graaf. Elke vector is een knooppunt met koppelingen naar nabijgelegen knooppunten, en een zoekopdracht springt langs deze koppelingen richting de query in plaats van alles te lezen.
m bepaalt hoeveel koppelingen elk knooppunt behoudt. De documentatie van Faiss stelt het geheugengebruik van HNSW vast op (d * 4 + m * 2 * 4) bytes per vector en raadt aan om m tussen 4 en 64 te houden. Voer dit uit op 768 dimensies over een miljoen vectoren.
The data behind this chart
[
{
"label": "m = 8",
"link_bytes_per_vector": 64,
"total_gib": 2.92
},
{
"label": "m = 16 (default)",
"link_bytes_per_vector": 128,
"total_gib": 2.98
},
{
"label": "m = 32",
"link_bytes_per_vector": 256,
"total_gib": 3.1
},
{
"label": "m = 64",
"link_bytes_per_vector": 512,
"total_gib": 3.34
}
]De nuttige verrassing is de omvang van dat verschil. Van de standaard m = 16 naar m = 64 gaan voegt 512 bytes aan koppelingen per vector toe tegenover 3072 bytes aan vectordata, waardoor het totaal verschuift van 2.98 GiB naar 3.34 GiB. Dat is ongeveer 12 procent. Bij deze dimensies is m niet waar uw geheugen naartoe gaat. Dat zijn de vectoren.
Wat m werkelijk kost, is opbouw- en invoegtijd, omdat het plaatsen van een knooppunt betekent dat er gezocht en gekoppeld moet worden aan dat aantal buren. ef_construction is de grootte van de kandidatenlijst die de builder overweegt terwijl deze elk knooppunt plaatst. Het verhogen hiervan levert een betere graaf op en een tragere opbouw, en het verandert de uiteindelijke indexgrootte helemaal niet.
SET maintenance_work_mem = '4GB';
SET max_parallel_maintenance_workers = 7;
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);maintenance_work_mem is de instelling die bepaalt of de opbouw minuten of uren duurt, omdat pgvector de graaf in het geheugen samenstelt wanneer dit past. Wanneer het niet meer past, meldt pgvector dit:
NOTICE: hnsw graph no longer fits into maintenance_work_mem after 100000 tuples
DETAIL: Building will take significantly more time.
HINT: Increase maintenance_work_mem to speed up builds.Die melding is de meest nuttige regel die pgvector afdrukt. Het betekent dat de opbouw is teruggevallen op een veel trager pad; annuleer deze dus, verhoog de instelling tot boven de RAM-waarde die u hierboven heeft berekend, en begin opnieuw. Monitor de voortgang vanuit een tweede sessie:
SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS pct
FROM pg_stat_progress_create_index;HNSW rapporteert initializing en vervolgens loading tuples. Een opbouw die lange tijd op een laag percentage blijft staan op een schijf die niet actief is, is het maintenance_work_mem-probleem, niet een vastgelopen query.
Twee feiten om rekening mee te houden. De README van pgvector stelt dat HNSW "tragere opbouwtijden heeft en meer geheugen gebruikt" dan IVFFlat, en daarvoor in ruil een betere verhouding tussen snelheid en recall biedt. En HNSW kan op een lege tabel worden aangemaakt, terwijl IVFFlat eerst k-means moet uitvoeren over representatieve data, waardoor het opbouwen op een lege tabel een slechte recall oplevert. Op een nieuw schema is HNSW degene die u vooraf kunt aanmaken.
ef_search: de instelling die u na de build aanpast
m en ef_construction zijn vastgelegd in de index. ef_search is dat niet. Deze instelling bepaalt hoeveel kandidaten de zoekopdracht behoudt tijdens het doorlopen van de graaf; u kunt dit per sessie of per query wijzigen.
SET hnsw.ef_search = 100;
SELECT id, body FROM chunks ORDER BY embedding <=> $1 LIMIT 5;De standaardwaarde is 40. Verhoog deze waarde om de recall te verbeteren, waarbij de latentie eveneens toeneemt. Verlaag deze waarde om beide te verminderen. Dit is de enige recall-instelling die u kunt aanpassen zonder een herbouw van de index. Stem deze daarom af op een vaste set queries waarvan u de juiste antwoorden al kent, en stop zodra de recall niet verder verbetert.
Let op één valkuil. ef_search werkt slecht samen met een selectieve WHERE-clausule, omdat de index een vast aantal kandidaten teruggeeft en het filter pas daarna wordt toegepast. Een filter dat de meeste rijen verwerpt, kan ertoe leiden dat u minder dan LIMIT resultaten overhoudt, terwijl er wel overeenkomende rijen in de tabel staan. pgvector 0.8 lost dit op met iteratieve scans:
SET hnsw.iterative_scan = relaxed_order;De index wordt vervolgens opnieuw gescand voor meer kandidaten totdat de limiet is bereikt, tot een maximum van hnsw.max_scan_tuples, wat standaard op 20000 staat. strict_order behoudt de exacte afstandsordening en kost meer rekenkracht. Dit is de functionaliteit die in het Ubuntu 0.6.0-pakket ontbreekt, en het ontbreken van rijen bij gebruik van een filter is hoe u merkt dat u deze versie gebruikt.
Waarom de index in het RAM moet passen
Een HNSW-zoekopdracht is een wandeling door een graaf. Elke stap leest een knooppunt dat op een willekeurige plek staat ten opzichte van het vorige, waardoor het toegangspatroon bijna willekeurig is en 'read-ahead' niet helpt. Zolang de graaf in het RAM staat, is elke stap een geheugenreferentie. Zodra dit niet meer het geval is, kan een stap een schijfleesactie worden, en verandert een zoekopdracht die enkele honderden knooppunten raakt in enkele honderden leesacties.
De documentatie van Qdrant verwoordt het als volgt: "als u de helft minder vectoren in het RAM opslaat, zal de zoeklatentie ruwweg verdubbelen." Houd rekening met deze stelling bij uw planning.
Wanneer de index echt niet past, is elke optie een afweging die u bewust moet maken.
- Gebruik memory-mapping voor de vectoren, zodat het besturingssysteem actieve pagina's in de cache houdt en inactieve pagina's op de schijf laat staan. Dit vereist snelle NVMe-opslag (non-volatile memory express) om acceptabel te blijven.
- Pas kwantisatie toe door elke dimensie als één byte op te slaan in plaats van vier. Dit vermindert het aantal bytes per vector met een factor vier, tegenover een kleine, meetbare afname in recall.
- Converteer naar
halfvecin pgvector, wat het aantal bytes halveert met minder verlies van recall dan bij kwantisatie naar één byte. - Gebruik een kleiner model voor de embeddings. Dit is de goedkoopste oplossing en de optie die mensen vaak overslaan, omdat het betekent dat de volledige corpus opnieuw moet worden verwerkt.
Foutmodi en de meldingen die u zult zien
could not open extension control file. Het pgvector-pakket voor de actieve Postgres-hoofdversie is niet geïnstalleerd. Toon de versie met sudo -u postgres psql -tAc 'SHOW server_version' en installeer de bijbehorende postgresql-NN-pgvector.
ERROR: expected 768 dimensions, not 1536. Het kolomtype en het model komen niet overeen. U heeft embedding-modellen gewijzigd en de data niet opnieuw verwerkt. Er is hier geen gedeeltelijke oplossing mogelijk, omdat vectoren van twee verschillende modellen totaal niet vergelijkbaar zijn; elke rij moet dus opnieuw worden gegenereerd.
De query is traag en EXPLAIN toont een sequential scan. De index-operatorklasse en de query-operator komen niet overeen. vector_cosine_ops bedient alleen <=>. Voer EXPLAIN ANALYZE uit op de query en zoek naar Index Scan using ... on chunks. Als u in plaats daarvan Seq Scan on chunks ziet, herbouw de index dan met de operatorklasse die overeenkomt met de operator die u daadwerkelijk gebruikt in uw query.
Minder rijen dan LIMIT, en er is een WHERE-clausule aanwezig. Dat is de hierboven genoemde filtervalstrik. Verhoog hnsw.ef_search, of stap over naar pgvector 0.8 en stel hnsw.iterative_scan in.
Het opbouwen van de index eindigt met een verdwenen proces en geen foutmelding in psql. maintenance_work_mem is ingesteld op het grootste deel van de machine, terwijl shared_buffers en uw applicatie ook geheugen vereisen; dit eindigt bij de out-of-memory killer van de kernel. sudo dmesg -T | grep -i 'killed process' toont de regel die postgres benoemt. Verlaag de instelling, of bouw de index op een groter plan en herstel de dump.
De keuze maken
Als u al Postgres gebruikt en u heeft minder dan enkele miljoenen vectoren, gebruik dan pgvector. De index bevindt zich naast de data, filteren gebeurt via een WHERE-clausule en uw bestaande back-up dekt deze al. Als de index groot genoeg is om een eigen geheugenlimiet te vereisen, of als u zware payload-filtering nodig heeft, draai dan Qdrant ernaast en accepteer dat u een tweede service moet beheren.
Bij minder dan ongeveer honderdduizend vectoren kunt u het beste een brute-force scan meten voordat u iets installeert. Een volledige zoekopdracht met perfecte recall en zonder opbouwfase is bij die omvang geen compromis. Het is de juiste oplossing; kiezen voor een benaderende index betekent dat u tuning en geheugendruk accepteert in ruil voor milliseconden die u voorheen niet kwijt was.
FAQ
Heb ik een dedicated vectordatabase nodig, of is Postgres voldoende?
Als uw data al in Postgres staat, is pgvector veel langer toereikend dan de meeste vergelijkingen suggereren. Het slaat vectoren op in een gewone kolom, waardoor een gefilterde zoekopdracht een WHERE-clausule is en uw bestaande back-up de index dekt. Stap over naar een dedicated opslag zoals Qdrant wanneer de vector-workload een eigen geheugenlimiet vereist, of wanneer u payload-filtering en kwantisatie nodig heeft die pgvector niet biedt.
Hoeveel vectoren passen er op één VPS?
Bereken dit in plaats van te gokken, met behulp van number_of_vectors * dimensions * 4 bytes * 1.5. Eén miljoen vectoren van 768 dimensies beslaan ongeveer 4,3 GiB, dus een 8 GB-plan kan dit aan met ruimte voor Postgres. Eén miljoen vectoren van 3072 dimensies beslaan ongeveer 17 GiB en vereisen een veel groter plan. De factor die dit het meest beïnvloedt is de dimensie van uw embedding-model; kies dat model dus met de geheugenkosten in gedachten.
Waarom is mijn vectorzoekopdracht traag als de index op dezelfde machine staat?
Het netwerk is niet het probleem op één machine, dus kijk naar de twee zaken die dat wel zijn. Meet eerst de embedding-aanroep afzonderlijk, omdat het genereren van de query-vector op de CPU vaak veel langer duurt dan de zoekopdracht zelf. Controleer ten tweede of de index in het RAM staat. Een HNSW-zoekopdracht springt willekeurig door een graaf; zodra de graaf naar de schijf wordt verplaatst, kan elke sprong een schijfleesactie worden. De richtlijn van Qdrant is dat het halveren van de vectoren in het RAM de zoeklatentie ongeveer verdubbelt.
Moet ik überhaupt een HNSW-index bouwen?
Niet bij minder dan ongeveer honderdduizend vectoren. Een volledige scan leest n * d * 4 bytes per query, wat 307 MB is bij 100.000 vectoren van 768 dimensies. Een moderne CPU streamt dit in enkele tientallen milliseconden met perfecte recall en zonder build-stap. Meet de scan eerst op uw eigen hardware. Bouw de index pas wanneer de gemeten scantijd daadwerkelijk te traag is, niet omdat een benchmark-artikel dat zegt.
Wat kost het verhogen van m mij daadwerkelijk?
Bouwtijd en invoegtijd, veel meer dan geheugen. Bij 768 dimensies voegt het verhogen van de standaard m = 16 naar m = 64 512 bytes aan graafverbindingen per vector toe tegenover 3072 bytes aan vectordata, waardoor het totale geheugengebruik met ongeveer 12 procent stijgt. Elke invoegactie moet echter vier keer zoveel buren vinden en koppelen. Stem eerst ef_search af, aangezien het wijzigen daarvan niets kost en geen herbouw vereist.