Skip to content

Commit 1d911a2

Browse files
Avery FeltsAvery Felts
authored andcommitted
feat: artifact disk cache — f32 holo + compact predictive, 14s→850ms warm search
1 parent 5f5f4ed commit 1d911a2

15 files changed

Lines changed: 492 additions & 47 deletions

File tree

Cargo.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -290,17 +290,16 @@ graphiq/
290290
### Storage Layout
291291

292292
```
293-
.graphiq/graphiq.db
294-
├── symbols # name, kind, signature, source, file, line
295-
├── edges # source, target, kind
296-
├── files # path, language, symbol_count
297-
├── edges_fts # FTS5 index (name, decomposed, hints, sig, source)
298-
├── manifest.json # artifact freshness tracking
299-
└── [computed at query time]
300-
├── spectral # Chebyshev heat diffusion infrastructure
301-
├── cruncher # adjacency lists, term sets, IDF
302-
├── predictive # conditional term models per symbol
303-
└── fingerprints # channel fingerprint vectors per symbol
293+
.graphiq/
294+
├── graphiq.db SQLite database (symbols, edges, FTS5 index)
295+
├── manifest.json artifact freshness tracking
296+
└── cache/ precomputed artifacts (zstd-compressed)
297+
├── cruncher.bin.zst
298+
├── holo_f32.bin.zst
299+
├── spectral.bin.zst
300+
├── predictive_compact.bin.zst
301+
├── fingerprints.bin.zst
302+
└── self_model.bin.zst
304303
```
305304

306305
### Operating System Support
@@ -313,7 +312,51 @@ graphiq/
313312

314313
## Benchmarks
315314

316-
See [docs/benchmarks.md](docs/benchmarks.md) for full results including per-category breakdowns, deep graph edge counts, and router performance analysis.
315+
v6 unified pipeline. 3 codebases (TypeScript, Rust, Go). Separate NDCG (20 queries/codebase) and MRR (25 queries/codebase) query sets.
316+
317+
### Accuracy vs Grep
318+
319+
| Metric | Grep | GraphIQ | Δ |
320+
|---|---|---|---|
321+
| MRR@10 | 0.941 | **0.959** | +1.9% |
322+
| NDCG@10 | 0.288 | **0.378** | +31% |
323+
| Combined | 0.615 | **0.669** | +8.7% |
324+
325+
| Category | Grep | GraphIQ | Winner |
326+
|---|---|---|---|
327+
| symbol-exact | 0.887 | **0.899** | GraphIQ |
328+
| symbol-partial | **0.711** | 0.708 | Grep (marginal) |
329+
| nl-descriptive | 0.069 | **0.289** | GraphIQ (4.2x) |
330+
| nl-abstract | 0.030 | **0.216** | GraphIQ (7.2x) |
331+
| error-debug | 0.159 | **0.268** | GraphIQ (1.7x) |
332+
| file-path | **0.066** | 0.048 | Grep |
333+
| cross-cutting | 0.000 | **0.137** | GraphIQ |
334+
335+
### Search Speed
336+
337+
All artifacts cached to disk after first query. Subsequent CLI searches load from cache.
338+
339+
| Metric | Time |
340+
|---|---|
341+
| Cold search (first run) | ~30s |
342+
| Warm search (cached) | **~850ms** |
343+
| In-process query (MCP/bench) | ~18μs |
344+
345+
### Artifact Cache
346+
347+
```
348+
.graphiq/cache/ Total: ~75MB
349+
├── cruncher.bin.zst 6.5MB adjacency lists, term sets, IDF
350+
├── holo_f32.bin.zst 42MB holographic name vectors (f32)
351+
├── spectral.bin.zst 17MB Chebyshev heat diffusion
352+
├── predictive_compact.bin.zst 8.8MB conditional term models (top-200/symbol)
353+
├── fingerprints.bin.zst 78KB channel fingerprint vectors
354+
└── self_model.bin.zst 356KB deterministic concept nodes
355+
```
356+
357+
Cache is validated against DB stats on every search — stale cache is transparently rebuilt. `graphiq reindex` and `graphiq upgrade-index` invalidate the cache automatically.
358+
359+
See [docs/benchmarks.md](docs/benchmarks.md) for full per-codebase breakdowns and methodology.
317360

318361
## Documentation
319362

crates/graphiq-bench/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "graphiq-bench"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
edition = "2021"
55

66
[[bin]]

crates/graphiq-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "graphiq-cli"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
edition = "2021"
55

66
[[bin]]

crates/graphiq-cli/src/main.rs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,34 +270,82 @@ fn cmd_search(
270270
let cache = graphiq_core::cache::HotCache::with_defaults();
271271
cache.prewarm(&db, 200);
272272

273-
let goober = graphiq_core::cruncher::build_cruncher_index(&db).ok().map(|ci| {
274-
let hi = graphiq_core::cruncher::build_holo_index(&db, &ci);
275-
(ci, hi)
276-
});
273+
let db_dir = db_path.parent().unwrap_or(db_path);
274+
let mut ac = graphiq_core::artifact_cache::ArtifactCache::new(db_dir, &db);
275+
276+
let goober = if let Some(ci) = ac.load::<graphiq_core::cruncher::CruncherIndex>("cruncher") {
277+
let hi = if let Some(cached_hi) = ac.load_holo() {
278+
cached_hi
279+
} else {
280+
let hi = graphiq_core::cruncher::build_holo_index(&db, &ci);
281+
ac.save_holo(&hi);
282+
hi
283+
};
284+
Some((ci, hi))
285+
} else {
286+
graphiq_core::cruncher::build_cruncher_index(&db).ok().map(|ci| {
287+
ac.save("cruncher", &ci);
288+
let hi = graphiq_core::cruncher::build_holo_index(&db, &ci);
289+
ac.save_holo(&hi);
290+
(ci, hi)
291+
})
292+
};
277293

278294
let mut engine = graphiq_core::search::SearchEngine::new(&db, &cache);
279295
if let Some((ref ci, ref hi)) = goober {
280296
engine = engine.with_goober(ci, hi);
281297
}
282298

283-
let spectral = graphiq_core::spectral::compute_spectral(&db).ok();
299+
let spectral = if let Some(si) = ac.load::<graphiq_core::spectral::SpectralIndex>("spectral") {
300+
Some(si)
301+
} else {
302+
let si = graphiq_core::spectral::compute_spectral(&db).ok();
303+
if let Some(ref s) = si {
304+
ac.save("spectral", s);
305+
}
306+
si
307+
};
284308
if let Some(ref spec) = spectral {
285309
engine = engine.with_spectral(spec);
286310
}
287311

288-
let predictive = graphiq_core::spectral::compute_predictive_model(&db).ok();
312+
let predictive = if let Some(pm) = ac.load_predictive() {
313+
Some(pm)
314+
} else {
315+
let pm = graphiq_core::spectral::compute_predictive_model(&db).ok();
316+
if let Some(ref p) = pm {
317+
ac.save_predictive(p);
318+
}
319+
pm
320+
};
289321
if let Some(ref pm) = predictive {
290322
engine = engine.with_predictive(pm);
291323
}
292324

293-
let (fp_vec, fp_id_map) = graphiq_core::spectral::compute_channel_fingerprints(&db);
325+
let (fp_vec, fp_id_map) = if let Some(cached) = ac.load::<(Vec<graphiq_core::spectral::ChannelFingerprint>, std::collections::HashMap<i64, usize>)>("fingerprints") {
326+
cached
327+
} else {
328+
let fps = graphiq_core::spectral::compute_channel_fingerprints(&db);
329+
ac.save("fingerprints", &(fps.0.clone(), fps.1.clone()));
330+
fps
331+
};
294332
engine = engine.with_fingerprints(&fp_vec, &fp_id_map);
295333

296-
let self_model = graphiq_core::self_model::build_self_model(&db).ok();
334+
let self_model = if let Some(sm) = ac.load::<graphiq_core::self_model::RepoSelfModel>("self_model") {
335+
Some(sm)
336+
} else {
337+
let sm = graphiq_core::self_model::build_self_model(&db).ok();
338+
if let Some(ref s) = sm {
339+
ac.save("self_model", s);
340+
}
341+
sm
342+
};
297343
if let Some(ref sm) = self_model {
298344
engine = engine.with_self_model(sm);
299345
}
300346

347+
ac.save_manifest(&db);
348+
301349
if debug {
302350
eprintln!("search mode: {}", engine.active_mode());
303351
}
@@ -517,6 +565,8 @@ fn cmd_reindex(path: &std::path::Path, db_path: &std::path::Path) {
517565

518566
let manifest = graphiq_core::manifest::build_manifest_all_ready(&db);
519567
let db_dir = db_path.parent().unwrap_or(std::path::Path::new("."));
568+
let ac = graphiq_core::artifact_cache::ArtifactCache::new(db_dir, &db);
569+
ac.invalidate();
520570
if let Err(e) = graphiq_core::manifest::write_manifest(db_dir, &manifest) {
521571
eprintln!(" warning: failed to write manifest: {e}");
522572
}

crates/graphiq-core/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
[package]
22
name = "graphiq-core"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
edition = "2021"
55

66
[dependencies]
77
rusqlite = { version = "0.34", features = ["bundled"] }
88
sha2 = "0.10"
99
serde = { version = "1", features = ["derive"] }
1010
serde_json = "1"
11+
bincode = "1"
12+
zstd = "0.13"
1113
rayon = "1.10"
1214
ignore = "0.4"
1315
thiserror = "2"

0 commit comments

Comments
 (0)