docs/design/openviking-cuvs-integration-plan.md
状态:功能集成与第一轮向量索引验证已完成,后续进入数据面性能化阶段。 原则:cuVS 是可选的 dense-search sidecar,不改变 OpenViking 默认 CPU 行为。 相关文档:benchmark 计划、初步结果、用户指南(中文)、User guide (English)。
本集成只替换 OpenViking 本地 VectorDB 的 dense vector search 执行器,继续复用现有的:
当前阶段需要做到:
local 行为;本阶段不做以下扩展:
| 决策 | 选择 | 原因 |
|---|---|---|
| 集成位置 | LocalIndex.search() 的 pure dense 分支 | 边界小,可复用 Store、恢复、过滤和回表 |
| 权威状态 | Local Store | GPU index 可随时由当前记录重建 |
| 默认算法 | cuVS brute-force | 适合功能对齐和 exact ground truth |
| ANN 算法 | CAGRA,显式开启 | 必须结合 Recall@K、延迟、build time 和显存评估 |
| mutation | host shadow 标脏,下一次查询全量重建 | 首先保证 upsert/delete 语义正确 |
| 过滤 | native bitmap 投影为 cuVS row bitset | 复用既有 DSL 和 scalar/path index,不重复实现语义 |
| 持久化 | 不持久化 cuVS index | 避免绑定 GPU、CUDA/cuVS 版本和序列化兼容性 |
| 默认 CPU dtype | 保持现有 per-vector-scale int8 | cuVS opt-in 不能改变未启用用户的行为 |
| GPU dtype | 当前使用 float32 shadow | 先建立正确性和性能基线;低精度作为独立能力评估 |
flowchart TD
API[OpenViking API / Service] --> Adapter[VectorDB Adapter]
Adapter --> Collection[LocalCollection]
Collection --> Store[(Local Store
single source of truth)]
Collection --> LocalIndex[LocalIndex]
LocalIndex --> Native[Native IndexEngine
dense / scalar / sparse / sort / aggregate]
LocalIndex --> Shadow[CuVSDenseIndex
host shadow / dirty state / label map]
Native --> Bridge[Native filter bitmap bridge]
Bridge -->|packed uint32 words| Shadow
Shadow --> GPU[cuVS + CuPy / CUDA
brute-force / CAGRA / prefilter]
Store -. restart recovery .-> Native
Store -. candidate reload .-> Shadow
一句话概括:OpenViking 管数据、生命周期和查询语义,cuVS 管 GPU dense top-k;两者通过 label layout 和 native filter bitmap 对接。
| 组件 | 职责 | 当前状态 |
|---|---|---|
VectorDBBackendConfig / CuVSConfig | backend、算法、显存预算、路由阈值和 CAGRA 参数校验 | 已实现 |
CuVSCollectionAdapter | 复用 local adapter,注入 cuVS dense-search 配置 | 已实现 |
LocalCollection | schema、Store、delta、恢复和结果回表 | 复用原生 |
LocalIndex | pure dense 路由、跨后端 mutation 一致性和 native fallback | 已扩展 |
CuVSDenseIndex | host shadow、dirty lifecycle、label map、bitset cache 和 GPU index | 已实现 |
_CuVSRuntime | 延迟导入 CuPy/cuVS,调用 brute-force/CAGRA build/search | 已实现 |
Native IndexEngine | 完整 native 能力和 filter bitmap 生成 | 已扩展 bitmap bridge |
| ABI3 backend | 暴露 filter layout 注册与 bitmap projection | 已实现 |
StoreManager | 权威记录、向量和字段存储 | 复用原生 |
| 状态 | 是否权威 | 生命周期 |
|---|---|---|
| Local Store | 是 | 持久化,恢复从这里开始 |
| Native index | 否 | 由 snapshot + delta 维护,承担完整本地能力 |
| cuVS host shadow | 否 | 按 label 保存 dense vector 和重建所需记录 |
| cuVS GPU index | 否 | 首次查询或 dirty 后构建,进程退出即丢弃 |
| Device filter cache | 否 | LRU 缓存,mutation 时清空 |
OpenViking 主键按现有规则映射为 uint64 label。cuVS 返回 dataset row id,CuVSDenseIndex 再通过构建时的 label 数组映射回 OpenViking label,最后由 Collection 回表得到完整记录。
sequenceDiagram
participant C as LocalCollection
participant N as Native IndexEngine
participant G as CuVSDenseIndex
participant S as Local Store
C->>S: 写入权威记录 / delta
C->>N: upsert 或 delete
N->>N: 更新 native index 并清空 filter layout
C->>G: 更新 host shadow
G->>G: dirty=true,清空 filter cache
Note over G: 下一次 GPU 查询同步重建 index
LocalIndex 使用跨后端重入锁覆盖 native mutation 和 cuVS shadow mutation,避免查询获得新 native bitmap 却使用旧 GPU row layout。当前 mutation 后不增量修改 CAGRA,而是标脏并在下一次 dense 查询全量重建。
flowchart TD
Q[LocalIndex.search] --> Pure{有 dense vector
且无 sparse terms?}
Pure -->|否| N[Native search]
Pure -->|是| Ready{cuVS 已配置且可用?}
Ready -->|否| N
Ready -->|是| Dirty{GPU index dirty?}
Dirty -->|是| Admit{auto 显存预算允许?}
Admit -->|否| N
Admit -->|是| Build[批量构建 GPU index
注册 label layout]
Dirty -->|否| Filter
Build --> Filter{存在 filter?}
Filter -->|否| GPU[cuVS top-k]
Filter -->|是| Bitmap[native scalar/path bitmap
投影为 cuVS row bitset]
Bitmap --> Route{auto 模式且候选数
低于 native 阈值?}
Route -->|是| N
Route -->|否| GPU
GPU --> Map[row id → label → record]
显式 backend=cuvs 对支持的 pure dense query 固定使用 GPU,并在初始化或运行错误时 fail-fast。auto_cuvs 才执行显存准入和候选数路由。
过滤路径不再扫描全部 Python records:
set_filter_layout(ordered_labels);evaluate_filter(dsl) 按已注册 layout 投影,返回 packed uint32 words 和 eligible count;filters.from_bitset() 构造 prefilter;该 bridge 继承 native 对以下能力的处理:
and、or;must、must_not;contains;range、range_out;date_time、geo_point 等 native 字段转换语义。bitmap 在 CPU 上生成是有意选择。native scalar/path index 已经维护了对应结构;把 URI DSL 解析、Trie 遍历和 bitmap union 搬到 GPU 会引入额外索引副本、同步和 kernel launch,而当前主要收益来自 GPU distance/top-k。只有 profiling 表明 bitmap 生成成为主要瓶颈时,才评估 GPU 化。
cuVS index 文件未来可以作为带严格版本约束的 cache,但不能成为事实来源。
| 查询类型 | 执行路径 |
|---|---|
| pure dense,无 filter | cuVS 或 auto/native |
| pure dense + scalar/path filter | native bitmap → cuVS prefilter,或 auto 路由 native recall |
| sparse / hybrid | native |
| scalar sort / aggregate | native |
| 空数据集 / filter 无候选 | 返回空结果 |
不采用 post-filter。先取未过滤 top-k 再过滤,无法保证高选择性过滤后的结果数量和真实 top-k;无限 over-fetch 也不能提供稳定正确性。
auto 模式包含两层决策:
当前默认阈值:
| 配置 | 默认值 | 含义 |
|---|---|---|
auto_filter_native_threshold | 2000 | 普通过滤候选数不超过该值时走 native recall |
auto_path_filter_native_threshold | 200 | URI/path 过滤使用的更保守阈值 |
URI/path 使用更低阈值,是因为宽路径需要 native Trie traversal 和 subtree bitmap union;这个成本会先于 GPU search 发生。阈值设为 0 可关闭对应 native 路由。阈值是当前测量得到的默认值,不是跨硬件、维度和 workload 的常数。
native fallback 在跨后端 GPU 锁外执行,以保留 native engine 原有的共享读并发。
因此:
低精度 GPU 存储作为显式新能力实现,不做隐式 cast:
当前保守估算包含:
N * dimension * 4;N * graph_degree * 4;N * intermediate_graph_degree * 4;N / 8;2.0 safety factor;若预算不足,auto 模式本次查询走 native,并保留 dirty 状态供后续查询重试。显式 backend=cuvs 不经过该 gate。估算不是硬保证,allocator、build algorithm、batch 和并发 workload 都可能改变实际 peak;已准入后的 allocation failure 仍会在 auto 模式回退 native。
{
"storage": {
"vectordb": {
"backend": "cuvs",
"distance_metric": "cosine",
"cuvs": {
"algorithm": "brute_force",
"fallback_to_native": true
}
}
}
}
{
"storage": {
"vectordb": {
"backend": "cuvs",
"cuvs": {
"algorithm": "cagra",
"build_params": {
"graph_degree": 64,
"intermediate_graph_degree": 128,
"build_algo": "nn_descent"
},
"search_params": {
"itopk_size": 64,
"search_width": 1
},
"fallback_to_native": true
}
}
}
}
{
"storage": {
"vectordb": {
"backend": "local",
"cuvs": {
"auto_enable": true,
"algorithm": "brute_force",
"auto_memory_reserve_mb": 1024,
"auto_memory_safety_factor": 2.0,
"auto_filter_native_threshold": 2000,
"auto_path_filter_native_threshold": 200,
"filter_cache_size": 16
}
}
}
}
| 配置 | 默认值 | 说明 |
|---|---|---|
algorithm | brute_force | brute_force exact 或 cagra ANN |
build_params | {} | 传给 CAGRA IndexParams |
search_params | {} | 传给 CAGRA SearchParams |
fallback_to_native | true | sparse/hybrid 等非 cuVS dense top-k 能力使用 native |
auto_enable | false | 在 backend=local 下按空闲显存自动启用 |
auto_memory_reserve_mb | 1024 | auto admission 后保留的显存 |
auto_memory_safety_factor | 2.0 | 已知 allocation 的峰值安全系数 |
auto_filter_native_threshold | 2000 | auto 普通过滤 native 路由阈值 |
auto_path_filter_native_threshold | 200 | auto URI/path native 路由阈值 |
filter_cache_size | 16 | device bitset 或 native 路由决策的 LRU 大小 |
依赖安装方式和 CUDA 12/13 wheel 选择见中英文用户指南,不在本计划中复制易过期的版本命令。
| 场景 | 显式 backend=cuvs | Auto mode |
|---|---|---|
| cuVS/CuPy 缺失或无 CUDA device | 初始化失败 | 保留 native |
| 向量维度错误 | 显式报错 | 显式报错 |
| 显存预算不足 | 不适用预算 gate | 本次查询 native,后续重试 |
| GPU allocation failure | 异常上抛 | 释放资源并回退 native |
| GPU build/search 其他异常 | 异常上抛 | 异常上抛 |
| sparse/hybrid | fallback_to_native=true 时 native | native |
| 空数据集 / filter 无候选 | 空结果 | 空结果 |
写链路仍跨 Store、native index 和 cuVS shadow。若进程在中间退出,重启会从 Store 收敛;若进程不退出且 shadow mutation 抛错,内存派生状态可能暂时分叉。后续可用统一 mutation journal、失败后强制 reload,或构建新 snapshot 后原子交换封闭窗口。
| 限制 | 影响 | 后续方向 |
|---|---|---|
| 每次 mutation 后整体重建 | 写后首查和 build 为 O(N) | base + delta、阈值重建、后台 build、原子切换 |
| host shadow 保存完整 dense vectors | Store/native/GPU 之外增加 host memory | 连续 buffer、共享内存、减少 Python object |
| 单 index GPU lock | 并发请求转化为排队延迟 | immutable GPU snapshot、CUDA streams、micro-batching |
| Python/CuPy 数据面 | 对象构造、复制和同步有固定开销 | 先 profiling,再决定是否下沉 C++ cuVS C API |
| 重启后 lazy rebuild | 大 collection 的首次查询延迟高 | 后台预热、版本化派生 cache |
| native dense 与 GPU shadow 并存 | CPU 内存和写放大 | 覆盖率和回退策略稳定后再评估裁剪 |
| CPU int8 / GPU float32 | 数值和内存语义不同 | 明确报告 dtype、Recall@K 和两侧 memory |
| auto admission 是估算 | 实际 peak 随 allocator 和 workload 波动 | telemetry、校准 safety factor、OOM fallback |
| 路由阈值来自当前 workload | 不能直接泛化 | 按规模、维度、filter type 做自适应或离线调参 |
完整矩阵与已有数据分别记录在 benchmark 计划和初步结果文档中。
优先级顺序:
只有 Python/CuPy 数据面被证明是主要瓶颈时,才评估:
backend=local 和 native dtype 不受影响。每组结果至少包括:
| 文件 | 职责 |
|---|---|
openviking/storage/vectordb/index/cuvs_index.py | runtime、host shadow、native bitmap 消费、cache、build/search 和 score mapping |
openviking/storage/vectordb/index/local_index.py | dense 路由、跨后端 mutation lock、native fallback |
openviking/storage/vectordb/collection/local_collection.py | 传递配置,恢复时注入当前 candidates |
openviking/storage/vectordb_adapters/local_adapter.py | cuVS adapter 与 auto 配置 |
openviking/storage/vectordb_adapters/factory.py | 注册 backend=cuvs |
openviking_cli/utils/config/vectordb_config.py | 强类型 cuVS 配置 |
src/index/detail/index_manager_impl.cpp | label layout 与 native bitmap projection |
src/abi3_engine_backend.cpp | filter bridge 的稳定 Python ABI |
examples/cuvs_smoke.py | 真实 GPU 最小功能验证 |
benchmark/cuvs/ | index、collection、service benchmark 与结果汇总 |
tests/vectordb/test_cuvs_index.py | cuVS index、filter cache、路由和错误测试 |
tests/vectordb/test_cuvs_collection.py | collection、auto mode、mutation 和恢复集成测试 |
tests/engine/test_index_engine.cpp | native bitmap 的外部 row-order projection 测试 |