Segmented vector database for approximate nearest neighbor search (HNSW, AVX2, mmap)
Project description
vextor
A segmented vector database for Approximate Nearest Neighbor search, written in C++20. Uses AVX2 SIMD distance kernels, HNSW graph indexing, and memory-mapped storage.
Vectors are written to an active in-memory segment, sealed to disk when full, and served as read-only mmap-backed segments. Search fans out across all segments and merges results.
Architecture
graph LR
A[core] --> B[store] --> C[index] --> D[segment] --> E[persistence]
| Layer | Contents |
|---|---|
| core | types, L2 distance (AVX2 + scalar, compile-time dispatch), SQ8 quantization |
| store | VectorStore concept, InMemoryStore, MmapStore |
| index | HnswIndex<Store>, FlatIndex<Store> |
| segment | ActiveSegment, SealedSegment, SegmentManager |
| persistence | Serializer, Loader, VEX0/HNSW/IDS binary formats |
Templates live in store/ and index/. Everything from segment/ up exposes only concrete types.
See docs/PRD.md for the full design rationale.
Code style
Code follows STL/snake_case naming convention: types in PascalCase, functions and variables in snake_case, namespace vextor in lowercase.
Build
Requires CMake 3.20+, Ninja, and a C++20 compiler (GCC 14+ or Clang 18+). Three presets are available:
| Preset | Description |
|---|---|
dev |
Debug build with ASan + UBSan |
release |
Optimized build |
release-python |
Optimized build + Python bindings |
cmake --preset release
cmake --build build-release
Run tests and benchmarks:
ctest --test-dir build-release --output-on-failure
./build-release/benchmarks/vextor_bench
SIFT1M benchmark (optional)
Requires the SIFT1M dataset (~160 MB download).
./benchmarks/sift1m/download.sh
cmake --preset release -DVEXTOR_BUILD_SIFT1M=ON
cmake --build build-release
./build-release/benchmarks/sift1m/vextor_sift1m
Results are written to benchmarks/sift1m/results.md.
Python bindings (optional)
Requires Python 3.8+ and NumPy.
Via pip (builds a wheel using scikit-build-core):
pip install .
python3 -c "import vextor; print('ok')"
On CPython ≥ 3.12 this produces an abi3 wheel that works across Python versions. Note: the wheel is built with the host compiler's AVX2 support — a wheel built on an AVX2 machine requires AVX2 at runtime.
Alternatively, as part of a CMake build:
cmake --preset release-python
cmake --build build-release-python
PYTHONPATH=build-release-python/python python3 -c "import vextor; print('ok')"
Usage
C++
#include <vector>
#include <vextor/vextor.h>
// In-memory only
vextor::Database db(/*dim=*/768, /*segment_capacity=*/1000000);
// Insert
std::vector<float> vec(768, 0.0f);
db.insert(/*user_id=*/42, vec);
// Search
std::vector<float> query(768, 1.0f);
auto results = db.search(query, /*k=*/10);
for (const auto& r : results) {
// r.user_id, r.distance
}
// With persistence
vextor::Database db2(768, 1000000, "path/to/db");
db2.insert(42, vec);
db2.save();
auto loaded = vextor::Database::load("path/to/db");
Python
import numpy as np
import vextor
db = vextor.Database(dimensions=768, segment_capacity=1_000_000, path="path/to/db")
db.insert(user_id=42, vector=np.random.randn(768).astype(np.float32))
results = db.search(query=np.random.randn(768).astype(np.float32), k=10)
for user_id, distance in results:
print(f" {user_id}: {distance:.4f}")
db.save()
db2 = vextor.Database.load("path/to/db")
Benchmarks
Release build, single-threaded. Selected results from local runs:
| Time | |
|---|---|
| L2 distance (scalar, 128d) | 43 ns |
| L2 distance (AVX2, 128d) | 9 ns |
| L2 distance (AVX2, 768d) | 80 ns |
| FlatIndex search (10K, 128d) | 163 μs |
| HNSW search (10K, 128d) | 39 μs |
| HNSW search (100K, 128d) | 145 μs |
HNSW is 4.2x faster than brute-force at 10K vectors. At 100K, HNSW search time grows sub-linearly (39 μs → 145 μs for 10x more vectors).
SIFT1M results
1M vectors, 128d float32, single-threaded, via SegmentManager (capacity 1.1M, no seal during build).
Machine: 12th Gen Intel(R) Core(TM) i7-1260P | 12 GB RAM | Linux 5.15.153.1-microsoft-standard-WSL2
| M | ef_construction | ef_search | Recall@1 | Recall@10 | Recall@100 | QPS | Build (s) |
|---|---|---|---|---|---|---|---|
| 16 | 200 | 64 | 0.9902 | 0.9903 | 0.9478 | 3503 | 603.7 |
| 16 | 200 | 128 | 0.9919 | 0.9941 | 0.9664 | 2810 | 603.7 |
| 16 | 200 | 256 | 0.9939 | 0.9986 | 0.9923 | 1523 | 603.7 |
| 32 | 400 | 128 | 0.9937 | 0.9985 | 0.9911 | 1605 | 1868.3 |
| 32 | 400 | 256 | 0.9940 | 0.9993 | 0.9986 | 944 | 1868.3 |
v0.1 gate (Recall@10 > 0.90): PASSED — alle 5 Configs erfüllen das Kriterium.
Project status
v0.1 — MVP. Single-node, single-threaded. See milestones for the roadmap.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file vextor-0.1.0.tar.gz.
File metadata
- Download URL: vextor-0.1.0.tar.gz
- Upload date:
- Size: 44.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afd973eb4cd6a68ab7b4911180d048f3653382cd6087d3ef58d6c87501e33ce0
|
|
| MD5 |
34cdae34fcec8d623c79a3221e756369
|
|
| BLAKE2b-256 |
ab6aab632e7115c75f717dfc8f1825a71fa19883505a778e2098e5ba662032ca
|
Provenance
The following attestation bundles were made for vextor-0.1.0.tar.gz:
Publisher:
publish.yml on mariorch22/vextor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vextor-0.1.0.tar.gz -
Subject digest:
afd973eb4cd6a68ab7b4911180d048f3653382cd6087d3ef58d6c87501e33ce0 - Sigstore transparency entry: 1782805006
- Sigstore integration time:
-
Permalink:
mariorch22/vextor@4b17c553d12c0c9eaae8bbaca0043ba35903caf5 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mariorch22
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4b17c553d12c0c9eaae8bbaca0043ba35903caf5 -
Trigger Event:
push
-
Statement type: