OverviewFilesCommitsIssuesDocsReleasesPeers

atomspace-gpu commits / 56fea4e

spec: GPU AtomSpace query architecture (incoming index + runQuery matcher)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
56fea4eaffd2c335d3b92c619b3b4c73c0d53ebe
by plankatron <fleeky@gmail.com> · 2026-06-26T01:38:57.000Z

Changed files (1) +84 -0

spec/gpu-query-architecture.md +84 -0
……@@ -0,0 +1,84 @@
1# GPU AtomSpace Query Architecture
2
3Status: IMPLEMENTED + verified (2026-06-25), branch `gpu-backing-store`.
4Scope: how a `GpuStorageNode`-backed AtomSpace stores atoms on the GPU, serves
5graph traversal from the GPU, and answers queries — with results verified
6byte-identical to the in-RAM opencog pattern matcher (`cog-execute!`).
7
8## Layers (bottom to top)
9
10### 1. GpuAtomTable — atoms in GPU RAM (Linas Steps 2/3)
11- AoS: one 12-byte `GpuAtom` per slot `{type, is_node, data_offset, data_len}`.
12- Variable-length side pools: `name_pool` (node names), `out_pool` (link outgoing
13 slot-ids). N-ary links; links may reference links (link-of-links).
14- C API: `gpu_store_node/link`, `gpu_fetch_node/link`, alloc/free/clear/barrier.
15- No ContentHash / flags / Values (deferred — Linas Step 4 is the Values
16 slice-table, still open).
17
18### 2. Incoming-set index (CSR) — the traversal primitive
19The pattern matcher's hot operation is "what links reference this atom?" (the
20incoming set). Built on demand from the stored links:
21- `gpu_table_build_incoming(t)` — count → host exclusive-scan → scatter, producing
22 CSR `in_off[CAPACITY+1]` + `in_pool[]` (device). Two CUDA kernels
23 (`k_count_incoming`, `k_scatter_incoming`).
24- `gpu_fetch_incoming(t, slot, buf, max, *count)` — incoming link-slots of an atom.
25- `gpu_intersect_incoming(t, a, b, ...)` — GPU-resident join: links referencing
26 BOTH a and b (one block, thread-strided membership test).
27- Total incoming entries == total outgoing entries → `in_pool` sized to `out_pool`.
28
29### 3. GpuStorageNode — BackingStore integration
30Implements the real opencog `BackingStore`/`StorageNode` contract:
31- `storeAtom` (recursive, outgoing-first), `loadAtomSpace` (two-pass nodes→links),
32 `storeAtomSpace`, `barrier`. TLB maps Handle↔slot (slot = UUID).
33- `fetchIncomingSet`/`fetchIncomingByType` — served from the GPU incoming index:
34 Handle→slot → `gpu_fetch_incoming` → reconstruct each link (`gpu_fetch_link` +
35 TLB slot→Handle) into the target AtomSpace, type-filtered. Lazy
36 `ensure_incoming()`; `do_store_atom` invalidates the index.
37- `storeValue`/`loadValue` are no-ops (no Value persistence yet) so the default
38 `runQuery` caching path does not throw.
39
40### 4. runQuery — GPU-resident matcher + fallback
41`runQuery(query, key, meta, fresh)`:
421. `gpu_run_meet(query, key)` — GPU fast-path (below). If handled, result placed
43 at `key` as a UnisetValue; return.
442. else `BackingStore::runQuery(...)` — the default crawl, which still pulls
45 incoming sets from the GPU via `fetchIncomingByType` (so it is GPU-served).
46
47#### GPU fast-path supported class
48`(Meet <vardecl> body)` with **exactly one variable**, body = a single clause or
49an `AndLink` of clauses, each clause one of:
50- (a) direct binary link `(T $X c)` / `(T c $X)` — any binary LinkType (e.g.
51 InheritanceLink, MemberLink);
52- (b) Eval relation `(Evaluation (Predicate P) (List $X c))` / `(List c $X)`.
53
54Per clause, candidate var-bindings are computed via the GPU incoming index
55(shape (a): one hop; shape (b): two hops, List then Eval, filtered by predicate).
56The answer is the **intersection** of the per-clause candidate sets. Result atoms
57are resolved via the TLB and returned as a UnisetValue at `key`.
58
59Anything else (multi-variable joins, nesting, globs, negation, …) returns false
60→ default path. Correctness is always preserved by the fallback.
61
62## Verification (tests/persist/gpu/)
63- `test_incoming.cu` — incoming-set + `gpu_intersect_incoming` exact on a
64 hand-built graph incl. link-of-link.
65- `GpuBasicSaveUTest` (12 cases): store/load/roundtrip; `fetchIncomingByType`;
66 **gold check** (GPU query == in-RAM `cog-execute!`, identical sets); V1 crisp
67 multi-hop; V4 neurosymbolic 3-fact composition; agent loop (transitive closure
68 persisted on GPU); `runQuery` GPU override; **general multi-clause** runQuery
69 (is-a mammal AND lives-in water → {whale,dolphin,seal}).
70- Scale (standalone, gpu-atomese-test): 1M atoms, ~90M two-clause intersections/s.
71
72## Known limits / next steps
731. **Multi-variable joins** (chains like R($X,$Y) ∧ S($Y,$Z)) fall back to the
74 default engine. Next: a backtracking / worst-case-optimal-join GPU matcher.
752. **Intersection is host-side** of GPU-fetched candidate sets; the heavy incoming
76 traversal is GPU. A full CUDA-kernel multiway intersection (cf. the standalone
77 1M-scale proto) is the perf upgrade — needs per-relation CSR on device.
783. **Values slice-table** (Linas Step 4) unimplemented → no Value persistence;
79 `storeValue`/`loadValue` are no-ops. This is the genuinely-open design question
80 (what algorithm drives the slice-table).
814. RT cores are deliberately NOT used: BVH traversal is metric/spatial; the query
82 primitive (neighbor-list set-intersection) is control-flow-divergent and a
83 verified poor fit for RT (it only suits an embedding-based candidate pre-filter).
84 The matcher runs on CUDA cores; tensor cores are reserved for Values/SpGEMM.