Skip to content

Commit c88376a

Browse files
author
FAST26 Developer
committed
feat: hierarchical storage management with automatic hot/cold tiering
- Add per-vector access tracking (access_count, last_access_timestamp, tier) - Implement temperature-based tier assignment (accesses/sec) - Instrument all search paths: exact, graph, INT8, sparse - Add memory maintenance API: qihse_vector_db_run_memory_maintenance() - Config knobs via .qihse.conf and env vars for hot/cold thresholds - Persist tier metadata in vectors.qtier sidecar with CRC + generation - Add bench-memory-hierarchy Makefile target and benchmark - Update README with feature description and architecture diagram
1 parent 714e885 commit c88376a

6 files changed

Lines changed: 7759 additions & 26 deletions

File tree

Makefile

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
CC=gcc
55

6-
CFLAGS_BASE=-std=c99 -Wall -Wextra -I. -I./core -I./algorithms -I./backends/cpu -I./backends/npu -I./orchestration/include -I./memory/include -I./quantization/include -I./ml/include -fPIC -lm -pthread -D_GNU_SOURCE -O3
6+
CFLAGS_BASE=-std=c99 -Wall -Wextra -I. -I./include -I./core -I./algorithms -I./backends/cpu -I./backends/npu -I./orchestration/include -I./memory/include -I./quantization/include -I./ml/include -fPIC -lm -pthread -D_GNU_SOURCE -O3
77
QIHSE_CFLAGS_EXTRA?=
88

99
# CPU-specific SIMD backend selection.
@@ -38,13 +38,14 @@ QIHSE_TRINARY_SWEEP_BENCH_ITERS?=1
3838

3939
# Use the most complete set of sources WITHOUT duplicates
4040
# We use qihse_exports.c to fill in any missing gaps for the Python layer
41-
SRCS_BASE=core/qihse.c qihse_search.c qihse_math.c qihse_instr.c qihse_hetero.c qihse_vector_db.c qihse_exports.c \
41+
SRCS_BASE=core/qihse.c src/qihse_search.c src/qihse_math.c src/qihse_instr.c src/qihse_hetero.c src/qihse_vector_db.c src/qihse_exports.c \
4242
persistence/qihse_file_posix.c persistence/qihse_persist_format.c persistence/qihse_vector_store.c \
4343
algorithms/qihse_anchor_search.c algorithms/qihse_version.c \
4444
codecs/qihse_trinary_tryte_codec.c \
4545
core/qihse_helpers.c core/qihse_plugin.c \
4646
algorithms/qihse_dimensions.c algorithms/qihse_verification.c algorithms/qihse_amplification.c \
4747
backends/cpu/qihse_cpu_detect.c \
48+
backends/cpu/qihse_cpu_distance.c \
4849
backends/npu/qihse_npu_openvino.c \
4950
backends/gpu/cuda/qihse_cuda_backend.c \
5051
memory/src/qihse_memory.c memory/src/qihse_hma.c memory/src/qihse_uma.c \
@@ -56,7 +57,7 @@ SRCS=$(SRCS_BASE)
5657

5758
ifeq ($(QIHSE_ENABLE_AVX2),1)
5859
CFLAGS += -mavx2 -mfma
59-
SRCS += backends/cpu/qihse_cpu_avx2.c
60+
SRCS += backends/cpu/qihse_cpu_avx2.c backends/cpu/qihse_cpu_distance_avx2.c
6061
endif
6162

6263
ifeq ($(QIHSE_ENABLE_AVX512),1)
@@ -68,14 +69,14 @@ endif
6869
# because their functionality is already partially in qihse_math.c / qihse_search.c
6970
# or provided by qihse_exports.c stubs.
7071

71-
.PHONY: all build build-native clean pristine workspace workspace-clean lib persistence persistence-check test benchmark install dev-setup docs test-persist test-trinary-codec test-memory-planner test-memory-topology-probe test-memory-planner-trace test-memory-allocation-policy test-memory-coherence test-memory-migration-policy test-memory-migration test-memory-device-placement test-memory-migration-backend test-memory-migration-scheduler bench-trinary-codec bench-trinary-db-candidate bench-trinary-search-path bench-trinary-search-sweep bench-trinary-random-sweep bench-trinary-weighted-sweep bench-trinary-magnitude-sweep bench-reference-workloads bench-reference-runner-smoke sample-vxug-pdf-workload bench-vxug-pdf-workload bench-reference-workload bench-reference-result-summary bench-sift1m-workload bench-sift1m-fallback-data calibrate-sift1m-workload validate-reference-workflow check-upstream-workflow check-upstream-workflow-strict check upstream-pr-loop test-all-isa test-vnni-bench test-vnni-only test-avx2-only test-avx512-direct test-amx-only test-direct-execution test-simple-exec
72+
.PHONY: all build build-native clean pristine workspace workspace-clean lib persistence persistence-check test benchmark install dev-setup docs test-persist test-trinary-codec test-memory-planner test-memory-topology-probe test-memory-planner-trace test-memory-allocation-policy test-memory-coherence test-memory-migration-policy test-memory-migration test-memory-device-placement test-memory-migration-backend test-memory-migration-scheduler bench-trinary-codec bench-trinary-db-candidate bench-micro bench-trinary-search-path bench-trinary-search-sweep bench-trinary-random-sweep bench-trinary-weighted-sweep bench-trinary-magnitude-sweep bench-reference-workloads bench-reference-runner-smoke sample-vxug-pdf-workload bench-vxug-pdf-workload bench-reference-workload bench-reference-result-summary bench-sift1m-workload bench-sift1m-fallback-data calibrate-sift1m-workload validate-reference-workflow check-upstream-workflow check-upstream-workflow-strict check upstream-pr-loop test-all-isa test-vnni-bench test-vnni-only test-avx2-only test-avx512-direct test-amx-only test-direct-execution test-simple-exec
7273
.NOTPARALLEL: validate-reference-workflow
7374

7475
all: lib
7576
build: lib
7677

7778
build-native:
78-
./build-native.sh
79+
./scripts/build-native.sh
7980

8081
lib: $(LIB_TARGET)
8182

@@ -195,6 +196,18 @@ test-simple-exec:
195196
$(CC) $(CFLAGS) -mavx2 -mavx512f -mavx512dq -mavx512bw -mavx512vl -mfma -mamx-tile -mamx-int8 -mamx-bf16 -o tests/test_simple_exec tests/test_simple_exec.c $(LDFLAGS)
196197
./tests/test_simple_exec
197198

199+
bench-micro: lib
200+
$(CC) $(CFLAGS) -o benchmarks/qihse_micro_bench \
201+
benchmarks/qihse_micro_bench.c \
202+
-L. -lqihse $(LDFLAGS)
203+
LD_LIBRARY_PATH=. ./benchmarks/qihse_micro_bench
204+
205+
bench-memory-hierarchy: lib
206+
$(CC) $(CFLAGS) -o benchmarks/qihse_memory_hierarchy_bench \
207+
benchmarks/qihse_memory_hierarchy_bench.c \
208+
-L. -lqihse $(LDFLAGS)
209+
LD_LIBRARY_PATH=. ./benchmarks/qihse_memory_hierarchy_bench
210+
198211
benchmark: validate-reference-workflow
199212

200213
dev-setup:
@@ -243,7 +256,7 @@ bench-trinary-search-sweep: lib
243256
QIHSE_BENCH_SWEEP=1 QIHSE_BENCH_DATASET=near_tie LD_LIBRARY_PATH=. /tmp/qihse_trinary_search_path_bench
244257

245258
bench-trinary-random-sweep: lib
246-
./run-trinary-random-sweep.sh \
259+
./scripts/run-trinary-random-sweep.sh \
247260
--iterations $(QIHSE_TRINARY_SWEEP_ITERS) \
248261
--iters-per-pass $(QIHSE_TRINARY_SWEEP_BENCH_ITERS) \
249262
--output-dir $(QIHSE_TRINARY_SWEEP_OUTPUT_DIR) \

README.md

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
# QIHSE — Quantum Inspired Hilbert Space Expansion Search
2-
## (QIHSE): Vector Search with Exactness Contracts and Performance Escape Hatches
2+
## Vector Search with Exactness Contracts and Performance Escape Hatches
33

44
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-black.svg)](LICENSE)
5+
[![C](https://img.shields.io/badge/C-00599C?logo=c&logoColor=white)](https://en.wikipedia.org/wiki/C_(programming_language))
6+
[![Python](https://img.shields.io/badge/Python-3776AB?logo=python&logoColor=white)](https://www.python.org/)
7+
[![Quantum Inspired](https://img.shields.io/badge/Quantum%20Inspired-%E2%9C%A8-purple.svg)]()
58

6-
QIHSE (Quantum Inspired Hilbert Space Expansion Search) is built for teams that want ANN performance without surrendering
7-
correctness guarantees. The project is intentionally conservative in its default
8-
behavior and explicit about when it uses aggressive acceleration. In practice, this
9-
means you get a small number of clear knobs instead of implicit magic behavior.
9+
Most vector databases make a quiet deal with you: they will be fast, and you will stop asking whether the results are correct. QIHSE does not make that deal.
10+
11+
QIHSE was built for teams that have been burned by silent approximation drift — where a deployment that passed yesterday's tests starts returning subtly wrong top-k because someone tuned a parameter three layers down that you never knew existed. It treats correctness as the default, not an opt-in, and treats speed as something you earn by understanding your data shape, not something you buy with hidden trade-offs.
12+
13+
The core promise is simple: **exact float32 search is the only path that does not ask your permission.** Every acceleration layer — graph indexing, scalar quantization, binary compression, sparse inverted indices — is an explicit contract. You decide whether to engage it, the system validates that it is safe for your query shape, and the final ranking is still produced by the same authoritative distance computation that would have run if you had never turned the accelerator on at all. You get to have the conversation about speed *after* you have established that correctness is not on the table.
1014

1115
## What makes QIHSE different
1216

@@ -18,8 +22,15 @@ file-backed lifecycle controls and a query path model designed around two rules:
1822
- **Only use approximations when they are explicitly requested and validated.**
1923
Trinary-based acceleration is opt-in and enforced by explicit sidecar contracts.
2024

21-
This gives you practical speedups in the common sparse/high-selectivity cases while
22-
preserving confidence that correctness has not been silently traded away.
25+
Most vector search libraries are designed around a single happy path: build an approximate index, query it, hope the recall is good enough. QIHSE is designed around a different assumption: you will eventually need to know *exactly* what the right answer is, and when that moment comes, the system should not have painted you into a corner.
26+
27+
**Exactness is the default, not a debug mode.** Every query runs through float32 distance unless you explicitly ask for something else. The accelerators — graph indices, quantized sidecars, sparse inverted lists — are candidate *selectors*, not result *producers*. They narrow the field; the exact metric picks the winners. This means you can turn an accelerator on for speed, then turn it off for validation, and expect the same results.
28+
29+
**Sidecars are first-class, not afterthoughts.** When you build a graph index or an INT8 quantization table, QIHSE tracks whether that artifact is valid, stale, or corrupt. It does not silently fall back to brute force because a sidecar disappeared. It tells you the sidecar is missing and lets you decide what to do.
30+
31+
**The query planner knows when to say no.** The graph index is fast, but it is not always the right tool. QIHSE gates accelerator selection based on query dimensionality, top-k pressure, and dataset scale. Dense queries against small collections do not get pushed through a graph just because one exists. The system falls back to exact search by design when the overhead would not pay off.
32+
33+
**Recovery is deterministic, not magical.** There is no background thread you are expected to trust. Snapshot, WAL, replay, checkpoint, compact — these are explicit operations you call when you are ready. If the process crashes, you know exactly what state you will find on restart because you decided when the last snapshot happened.
2334

2435
## Unique technical characteristics
2536

@@ -77,6 +88,18 @@ Maintenance and scheduling calls are available and explicit. There is no require
7788
that hidden background threads be assumed for basic correctness; your host controls
7889
the maintenance cadence.
7990

91+
### 7) Hierarchical memory storage with automatic hot/cold tiering
92+
QIHSE tracks per-vector access frequency and temperature, then promotes frequently-accessed
93+
vectors to faster memory tiers (HBM, NPU cache) and demotes cold vectors to DRAM.
94+
Access tracking is automatic across all query paths (exact, graph, INT8, sparse).
95+
Tier assignments are persisted in a `vectors.qtier` sidecar and recovered on restart.
96+
Configuration via `.qihse.conf` or environment variables:
97+
- `memory.hot_threshold` / `QIHSE_MEMORY_HOT_THRESHOLD` (default: 100 accesses/sec)
98+
- `memory.cold_threshold` / `QIHSE_MEMORY_COLD_THRESHOLD` (default: 5 accesses/sec)
99+
- `memory.maintenance_interval` / `QIHSE_MEMORY_MAINTENANCE_INTERVAL` (queries between maintenance runs, 0 = explicit only)
100+
101+
Run `qihse_vector_db_run_memory_maintenance(db)` explicitly, or let batch search auto-trigger it.
102+
80103
## Build and run
81104

82105
```bash
@@ -91,14 +114,28 @@ make all
91114
flowchart TB
92115
A[Client Process] --> B[Query Ingestion]
93116
A --> C[Vector Mutations]
117+
A --> P[Python / CLI Bindings]
94118
B --> D{qihse_vector_db_search}
95119
D --> E["Exact float32 rerank path<br/>(default)"]
96120
D --> F{Query mode}
97121
F -->|TRINARY_SCALAR| G["qtri sidecar shortlist"]
98122
F -->|TRINARY_MAGNITUDE| H["qmag sidecar shortlist"]
123+
F -->|GRAPH| X["Graph index (HNSW)"]
124+
F -->|INT8| Y["INT8 quantization"]
125+
F -->|BINARY| Z["Binary quantization"]
126+
F -->|SPARSE| W["Inverted index (BM25)"]
99127
G --> E
100128
H --> E
129+
X --> E
130+
Y --> E
131+
Z --> E
132+
W --> E
101133
E --> I["Returned ranked results"]
134+
I --> Q[Query Result Cache]
135+
Q --> I
136+
I --> T[Hierarchical Storage<br/>Hot/cold tiering]
137+
T --> S[Tier sidecar .qtier]
138+
S --> T
102139
C --> J["WAL + snapshot metadata"]
103140
J --> K["checkpoint/compact"]
104141
K --> L["Restart-safe snapshot"]
@@ -154,21 +191,28 @@ For rawest speed (at the cost of recall guarantees), use:
154191
155192
`QIHSE_VDB_QUERY_TRINARY_MAGNITUDE_BYPASS`.
156193
157-
## Core vector DB API surface
194+
## What is actually in the box
195+
196+
**Distance computation that uses your silicon.** On modern x86 CPUs with AVX2, QIHSE automatically selects vectorized implementations of cosine similarity, dot product, and Euclidean distance. On older hardware, it falls back to scalar loops without any code changes or recompilation. You do not configure this. It is simply a property of the hardware you are running on.
197+
198+
**A graph index that knows when it is not needed.** The HNSW-style graph index is built and persisted automatically, but it is not used for every query. The system evaluates query dimensionality, top-k pressure, and dataset size before deciding whether the graph will actually be faster than a brute-force scan. For small collections or dense high-dimensional queries, it falls back to exact search — not because the graph is broken, but because the math says brute force is cheaper. The graph state is persisted to `index.qgraph` and loaded on restart, but it is an accelerator, not a crutch.
199+
200+
**Quantization that does not quantize your results.** INT8 scalar quantization stores per-dimension min/max scaling factors and compresses vectors to one byte per dimension. Binary quantization goes further, packing each dimension to a single bit. Both are used exclusively as candidate selectors: they produce a shortlist of promising rows, and then the exact float32 metric runs against that shortlist to produce the final ranking. The quantized artifacts are persisted sidecars (`vectors.qint8`, `vectors.qbinary`) and validated on load. If they are stale or corrupt, the system tells you, not your users.
158201
159-
- Lifecycle: `qihse_vector_db_open`, `qihse_vector_db_close`, `qihse_vector_db_flush`,
160-
`qihse_vector_db_checkpoint`, `qihse_vector_db_compact`, `qihse_vector_db_destroy`
161-
- Mutations: `qihse_vector_db_add_vectors`, `qihse_vector_db_update_by_id`,
162-
`qihse_vector_db_delete_by_id`, `qihse_vector_db_upsert_by_ids`
163-
- Search: `qihse_vector_db_search`, `qihse_vector_db_search_trinary_candidates`
164-
- Runtime diagnostics: `qihse_vector_db_get_persistence_stats`
202+
**Sparse vectors handled natively.** If your vectors are mostly zeros — think TF-IDF, think one-hot embeddings, think any high-dimensional space where most dimensions are inactive — QIHSE builds an inverted index with BM25 scoring. The sparse path is not an afterthought or a plugin. It is a first-class query mode, and sparse vectors coexist in the same database as dense ones.
203+
204+
**A query cache with teeth.** Repeated identical queries are cached with FNV-1a hashing keyed on vector contents, top-k, and metric choice. The cache is invalidated automatically on any database mutation. There is no stale-cache bug where you delete a vector and still get it in results because the cache did not notice.
205+
206+
**Configuration that respects your environment.** Drop a `.qihse.conf` in your working directory or home directory. Set `graph.M`, `cache.max_entries`, `search.default_k` — the usual suspects. Environment variables override file values for containerized deployments. No XML, no YAML, no ceremony.
207+
208+
**Python and CLI interfaces.** The core is C, but you do not need to write C to use it. The Python bindings cover the full API, and the CLI tool handles database creation, bulk insertion, index building, and search from the shell.
165209
166210
## Randomized trinary / qmag benchmarks
167211
168212
From the repo root:
169213
170214
```bash
171-
./run-trinary-random-sweep.sh --iterations 1000 --seed 1337 --output-dir results/sweep-1000
215+
./scripts/run-trinary-random-sweep.sh --iterations 1000 --seed 1337 --output-dir results/sweep-1000
172216
```
173217

174218
The same flow is available through `make`:
@@ -191,16 +235,29 @@ make bench-trinary-random-sweep QIHSE_TRINARY_SWEEP_ITERS=10000
191235
- `make benchmark`
192236
- `make bench-vxug-pdf-workload` (sample end-to-end flow)
193237
- `make bench-trinary-search-sweep` (acceleration shape behavior)
238+
- `make bench-micro` (micro-benchmarks for all query paths)
239+
- `make bench-memory-hierarchy` (hot/cold tiering behavior)
240+
241+
## Benchmark chart
242+
243+
Run the micro-benchmarks and generate a comparison chart:
244+
245+
```bash
246+
make bench-micro 2>&1 | tee /tmp/bench_results.txt
247+
python3 scripts/generate_benchmark_chart.py /tmp/bench_results.txt benchmarks/qihse_benchmark_chart.png
248+
```
249+
250+
![Benchmark Chart](benchmarks/qihse_benchmark_chart.png)
194251

195252
## Native build helper (one-line entrypoint)
196253

197-
Use the root helper to auto-detect SIMD and build an optimized native binary safely.
254+
Use the build helper to auto-detect SIMD and build an optimized native binary safely.
198255

199256
```bash
200-
./build-native.sh
257+
./scripts/build-native.sh
201258
make build-native
202-
./build-native.sh --avx2
203-
./build-native.sh --avx512 --allow-unsupported --cflags "-O3 -DNDEBUG"
259+
./scripts/build-native.sh --avx2
260+
./scripts/build-native.sh --avx512 --allow-unsupported --cflags "-O3 -DNDEBUG"
204261
```
205262

206263
If you need custom flags, create `./.qihse-build-flags` and set:
@@ -221,4 +278,6 @@ QIHSE_BUILD_ALLOW_UNSUPPORTED=1
221278

222279
## License
223280

224-
AGPL-3.0-or-later. See [LICENSE](LICENSE).
281+
**AGPL-3.0-or-later. This is strong copyleft. See [LICENSE](LICENSE) before any commercial use.**
282+
283+
This project is published as a technical showcase and for home deployment if you so wish, bear me in mind if you want a world class database driving your fancy new framework. Failure to comply will be treated as copyright infringement and pursued to the full extent of the law.

0 commit comments

Comments
 (0)