OpenViking 的 cuVS GPU 向量检索后端:配置、显存准入与本地向量搜索实战
2026/9/11 20:17:46 网站建设 项目流程

OpenViking 的 cuVS GPU 向量检索后端:配置、显存准入与本地向量搜索实战

【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking

OpenViking 的cuvs向量后端把 dense vector search 交给 NVIDIA cuVS,同时保留本地后端的记录持久化、标量索引、稀疏检索和故障恢复,是验证 GPU 检索链路而不重写整个向量数据库的轻量集成方式。本文覆盖 cuVS 后端的环境要求与安装、brute_force/CAGRA 算法配置、显存感知自动模式(auto admission)、微批处理(micro-batching)、GPU 显存占用估算、数据类型与原生索引行为边界,以及最小功能验证方法;读完后你可以直接在自己的 GPU 机器上配置并跑通 OpenViking 的 cuVS 检索链路。

架构定位:cuVS 只是索引库,不是完整向量数据库

在动手配置之前,先理解这个后端在 OpenViking 中的职责边界。cuVS 是 NVIDIA 的 GPU 向量索引库,只负责 dense vector top-k 搜索;OpenViking 的本地引擎仍然负责 durable records、标量/路径索引、稀疏检索和崩溃恢复。从源码结构看,这一边界在 cuvs_index.py 的模块文档字符串中被明确写出:

cuVS is an index library rather than a complete vector database. This module therefore owns only the dense vectors and their label mapping. OpenViking's existing local engine remains responsible for durable records, scalar indexes, sparse retrieval, and crash recovery.

首版实现刻意追求正确性和简单的生命周期语义:upsert 与 delete 更新 host 侧 snapshot 并使 GPU 索引失效,下一次搜索时一次性批量重建 cuVS 索引。这样即使 cuVS 对不同索引类型没有统一的 update/delete 契约,brute-force 和 CAGRA 都能兼容 OpenViking 的全部 mutation 路径。

在 backend 工厂中,cuvslocalhttp等并列注册为合法 backend:factory.py 的_ADAPTER_REGISTRY"cuvs"映射到CuVSCollectionAdapter。该 adapter 继承自LocalCollectionAdapter,在构造时把配置注入为"dense_search": {"backend": "cuvs", ...}的 collection 配置(见 local_adapter.py)。所有配置字段的默认值、取值范围与约束则由 Pydantic 模型 CuVSConfig 统一定义,且model_config = {"extra": "forbid"},意味着配置中出现未声明的字段会直接报错。

环境要求与安装

运行 cuVS 后端需要满足以下条件:

  • Linux x86_64 或 aarch64
  • 可见的 NVIDIA GPU;cuVS 26.06 预编译包要求 Ampere 或更新架构
  • CUDA 12.2+;安装与本机 CUDA 大版本匹配的 Python 包
  • Python 3.11+(cuVS 26.06 的 Python wheel 要求)

CUDA 12 环境:

pip install -e . pip install cuvs-cu12 'cupy-cuda12x[ctk]' --extra-index-url=https://pypi.nvidia.com

CUDA 13 环境:

pip install -e . pip install cuvs-cu13 'cupy-cuda13x[ctk]' --extra-index-url=https://pypi.nvidia.com

CuPy 的[ctk]extra 会安装 cuVS Python 互操作路径所需的 CUDA toolkit headers;即使宿主机已有 CUDA driver、但没有完整 toolkit,也建议保留该 extra。

配置:从 brute_force 精确检索到 CAGRA 近似检索

先用 brute_force 跑通精确检索

最小可用的 cuVS 配置如下,对应 CuVSConfig 中的默认值组合:

{ "storage": { "workspace": "/data/openviking", "vectordb": { "backend": "cuvs", "distance_metric": "cosine", "cuvs": { "algorithm": "brute_force", "dtype": "float32", "max_concurrent_gpu_searches": 1, "micro_batching_enabled": false, "fallback_to_native": true, "filter_cache_size": 16 } } } }

各参数的含义与默认值(均来自CuVSConfig源码定义):

参数类型/取值默认值说明
algorithm"brute_force"/"cagra""brute_force"先用 brute_force 做功能验证;大规模场景再用 cagra 做近似检索
dtype"float32"/"float16""float32"GPU dataset 和 query 的数据类型。float16 是显式 opt-in 的直通 cast,需自行 benchmark recall,不影响原生 CPU 量化
build_paramsdict{}透传给 cuVS CAGRAIndexParams的额外关键字参数
search_paramsdict{}透传给 cuVS CAGRASearchParams的额外关键字参数
fallback_to_nativebooltruesparse/hybrid 检索及 cuVS dense top-k 之外的操作回落到 OpenViking 原生本地索引
filter_cache_sizeint ≥ 016GPU 上保留的最近使用 scalar-filter bitset 缓存数量;0 表示禁用缓存
max_concurrent_gpu_searchesint ≥ 11每个索引的在途 cuVS GPU search 调用上限;host 侧 filter 与 snapshot 工作仍并发
micro_batching_enabledboolfalse把兼容的并发 cuVS dense 查询合并为一次 matrix-search 调用
micro_batching_max_batch_sizeint 1–88一次 cuVS search 调用最多携带的兼容查询数
micro_batching_max_wait_msfloat 0–1001.0scheduler 为收集兼容请求主动等待的窗口(毫秒);0 表示不主动等待
auto_enableboolfalsebackend 为local时,GPU 空闲显存充足则自动启用 cuVS dense 搜索
auto_memory_reserve_mbint ≥ 01024auto 准入预算之外保留的 GPU 空闲显存(MiB)
auto_memory_safety_factorfloat ≥ 1.02.0乘在估算的 vector/graph/build/filter 显存之上的保守系数
auto_filter_native_thresholdint ≥ 02000过滤查询候选数不超过该值时路由到 native 索引;0 关闭该路由
auto_path_filter_native_thresholdint ≥ 0200路径过滤使用更低的 native 路由阈值;0 让全部路径过滤留在 cuVS
auto_background_rebuildboolfalse由合并后的后台 worker 构建 dirty 状态的 auto-cuVS snapshot,期间查询走 native 索引
auto_rebuild_debounce_msint ≥ 0500连续 mutation 后触发后台 rebuild 前的静默窗口

配置模型还内置了 micro-batching 的一致性校验(validate_micro_batching):开启micro_batching_enabled时若algorithm不是brute_forcemax_concurrent_gpu_searches不等于 1,配置加载即抛错。

数据量增大后切换到 CAGRA

CAGRA 构建与查询参数可以直接传入 cuVS 原生接口:

{ "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 } } } } }

build_params中的graph_degreeintermediate_graph_degree不只是透传项——它们直接进入 auto 模式的显存估算:graph_degree决定常驻 graph 大小,intermediate_graph_degree决定构建期 intermediate graph 大小。

显存感知自动模式

如果希望保留local为默认 backend、只在 GPU 有足够空闲显存时自动启用 cuVS,可以打开以下开关:

{ "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, "auto_background_rebuild": true, "auto_rebuild_debounce_ms": 500 } } } }

注意一个实现细节:从源码结构看,auto_enable并不是把 backend 换成cuvs,而是 LocalCollectionAdapter.from_config 检测到该开关后,向 collection 注入"dense_search": {"backend": "auto_cuvs", ...}的 collection 级配置,使 dense 检索在每次查询时做 GPU 准入判断。

每次 lazy build/rebuild 前,auto 模式会读取当前空闲显存,并根据配置的dtype估算 device vector payload、CAGRA graph/intermediate graph(如适用)和 filter-bitset cache,再乘以auto_memory_safety_factor,同时保留auto_memory_reserve_mb。这套估算逻辑实现于 estimate_cuvs_memory,其公式与下文"GPU 显存占用"一节一致:向量字节数按N * dimension * (4 or 2)计,CAGRA 再加N * graph_degree * 4N * intermediate_graph_degree * 4,filter cache 按ceil(N/32) * 4 * filter_cache_size计。如果预算不足,或者 cuVS/GPU 不可用,本次查询继续使用未改变的 native index;cuVS index 保持 dirty,后续查询会在显存释放后重新尝试。通过 admission 后若仍遇到 GPU allocation failure,也会回退 native。显式配置backend: "cuvs"时仍保持 fail-fast,不经过这层自动判断。

同一进程内的 local collection 会按 GPU 协调 build 和 admission,避免两个并发 build 都基于同一份过期 free-memory 观测通过准入。不同 GPU 彼此独立,warmed search 也不会被这个协调器串行化。

auto 模式还会使用 native scalar index 返回的候选数做 filtered query 延迟路由:候选数不超过auto_filter_native_threshold时使用 native vector recall;路径过滤采用更低的auto_path_filter_native_threshold,因为宽 URI 子树的 Trie 遍历和 bitmap union 本身可能占主要开销。默认阈值分别为 2,000 和 200,设为 0 可关闭对应路由。阈值与硬件、维度和工作负载有关。显式backend: "cuvs"对支持的 dense query 仍固定使用 cuVS。

auto_background_rebuild默认关闭。开启后,连续 mutation 会按auto_rebuild_debounce_ms合并,worker 在不持有跨后端 mutation 锁的情况下构建新的 immutable GPU snapshot。默认 500 ms 用于避免普通 ingest 的中间 batch 反复触发构建。对于边界明确、由多次调用组成的 bulk load,可把所有写入放在async with backend.bulk_ingest(ctx=ctx):scope 内:native 可见性和持久化仍按每次调用推进,但 derived GPU maintenance 会延迟到最外层 scope 退出后只调度一次。该 scope 只是 maintenance hint,不提供事务或原子性;退出 scope 只负责调度 rebuild,本身不等待 GPU ready。vector backend benchmark 会额外在正式计时 search 前显式等待最终 snapshot;无法识别 bulk 边界的调用方仍可按实际 batch 间隔调整 debounce。

Auto 仍为显式启用;未开启 Auto/background rebuild 时,该 scope 对派生维护为 no-op,不改变原生 CPU 检索、写入与 dtype 行为。snapshot dirty 期间查询直接使用当前 native index,不会把 GPU build 时间转化成请求排队时间。worker 只在 record generation 仍匹配时原子提交 label layout 和 GPU snapshot;过期 build 会被丢弃,并只重建最新一代。

GPU 显存占用

使用默认的dtype: "float32"时,brute-force 的主要常驻 device payload 为N * dimension * 4bytes。显式设置dtype: "float16"后,device payload 降为N * dimension * 2bytes。CAGRA 还需要约N * graph_degree * 4bytes 保存 graph,构建期间可能需要N * intermediate_graph_degree * 4bytes 的 intermediate graph。每个缓存 filter bitset 约占ceil(N / 32) * 4bytes。

这些公式与 estimate_cuvs_memory 的逐项计算一一对应,说明文档中的显存预算并非经验数字,而是 auto admission 真实使用的估算器。

之前的 index-only 测试使用cudaMemGetInfo记录 build 前后的显存增量;下表每项均为 5 个干净进程的中位数:

数据集cuVS 算法实测 GPU 增量
100K x 768Dbrute-force294 MiB
1M x 768Dbrute-force2.9 GiB
100K x 1024Dbrute-force392 MiB
1M x 1024Dbrute-force3.9 GiB
1,183,514 x 100Dbrute-force452 MiB
1,183,514 x 100DCAGRA872 MiB

这些数值是 build 完成后的常驻增量,不是采样得到的 peak VRAM。allocator 状态、cuVS 版本、CAGRA 参数、query batch 和并行 GPU workload 都可能进一步提高峰值;它们也不包含这些进程在 build 前观测到的约 327 MiB CUDA runtime/context 基线。因此 auto 模式会先初始化 runtime、读取剩余空闲显存,再应用保守 safety factor 和独立 reserve,而不会只按 vector payload 准入。

距离语义与原本的 OpenViking 本地后端保持一致:cosine 会先做 L2 归一化再执行 inner product;L2 的返回分数仍为1 - squared_l2,分数越大越相似。

数据类型与原生索引行为

启用 cuVS 不会改变 OpenViking 的默认后端,也不会重写原生 CPU 索引。正常的 collection metadata 仍为VectorIndex.Quant=int8,因此 native fallback 继续使用现有的、带逐向量 scale 的 int8 量化。与此同时,cuVS device dataset 和 query 使用配置的dtype:默认是 float32,也可以显式选择 float16。host record shadow 保存预处理后的 Python 浮点值;仅在创建 device dataset 和 query 时将它们 cast 为配置的 dtype。cuVS Python brute-force API 支持这两种 device 表示,但不能直接表示 OpenViking 的 scaled-int8 record 格式。

所以两条 dense search 路径不是等内存、等数值语义的比较:native 是在 CPU 量化表示上的精确检索,cuVS brute-force 是在保留的 float32 或 float16 device 表示上的精确检索,两者可能出现少量 score 或 neighbor ordering 差异。Benchmark 必须同时报告两边的数据类型和 Recall@K,不能将结果描述为 equal-dtype 或 equal-memory。这是首版 opt-in 集成的有意边界,现有 CPU 行为保持不变。auto 模式会根据 filter 候选阈值在两种表示之间选择;要求固定数值表示的应用应使用显式 backend,或将 native 路由阈值设为 0。

GPU 低精度存储是显式能力,不做隐式 cast。设置dtype: "float16"会把 cuVS dataset 和每个 query 同时 cast 为 float16,brute-force 与 CAGRA 都不使用混合 query/index dtype。这是存储 cast,不是逐向量量化,必须以默认 float32 为 ground truth 报告 Recall@K。与 native 兼容的 int8 仍需单独设计,因为 OpenViking 使用逐向量 scale,而 cuVS brute-force 不能直接接收这种 scaled-int8 表示。CAGRA int8 或 PQ compression 也应作为近似模式,单独报告 recall/latency/memory frontier。

GPU 索引使用 immutable snapshot 和可复用的 cuVS resource/CUDA stream。host 侧 filter 与 snapshot 工作可以并行,但max_concurrent_gpu_searches默认是 1:单 query brute-force 通常受显存带宽限制,并发 kernel 可能互相争抢带宽、反而降低吞吐。只有在目标 GPU 与真实 workload 上测得收益后,才建议显式调大该值。

可选的请求微批处理

精确 brute-force 路径可以把兼容的并发请求合并为一次 cuVS matrix-query 调用:

{ "storage": { "vectordb": { "backend": "cuvs", "cuvs": { "algorithm": "brute_force", "max_concurrent_gpu_searches": 1, "micro_batching_enabled": true, "micro_batching_max_batch_size": 8, "micro_batching_max_wait_ms": 1.0 } } } }

scheduler 只会合并使用同一个 immutable GPU snapshot、同一个 prepared filter、同一个实际 top-k 的请求;GPU 返回的每一行会映射回原请求,因此标量/路径过滤和结果条数语义不变。

当 immutable snapshot clean、属于当前 generation,且请求没有 filter 或命中已准备好的 device filter cache 时,可走 warm admission fast path。该路径会 pin snapshot/filter,并在 caller 不获取 device-search gate 的情况下直接入队。dirty、cold 或 stale snapshot,device filter cache miss/eviction、rebuild 和 device filter materialization 仍走 gated preparation。准备完成后,caller 先入队并释放 gate,再等待结果;只有 micro-batch worker 会在持有 device-search gate 时执行 matrix search,所以 caller 不会持 gate 等待 worker。

collection window 是延迟与吞吐的权衡。它只限制 scheduler 为收集兼容请求而主动等待的时间:从最早的 compatible request 起最多主动等待配置值;它不是 enqueue-to-dispatch latency 上限。worker 调度、前一个 GPU call 或 gated device preparation 都可能使实际 dispatch 更晚。并发充足时,最多由配置上限数量的 query 共用一次 GPU call。

参数约束如下:

  • micro_batching_max_batch_size范围为 1 到 8;
  • micro_batching_max_wait_ms范围为 0 到 100 ms;设为0表示不主动等待,但仍可 opportunistically 合并已经同时在队列中的兼容请求;
  • micro-batching 仅支持algorithm: "brute_force",并要求max_concurrent_gpu_searches: 1

这些约束在配置层就有强制:CuVSConfig.validate_micro_batching 会在algorithm != "brute_force"max_concurrent_gpu_searches != 1时拒绝配置。

该能力默认关闭,是 OpenViking 自己的 micro-batcher,不等同于 cuVS 官方名为 Dynamic Batching 的组件。首版只支持 exact brute-force;CAGRA 和并发 dispatch 多个 batch 会在独立验证后再开放。Auto 模式也可使用这些选项,但被路由到原生 CPU 的请求不会进入 GPU batch queue。single-row 与 matrix-query 在近似并列分数处可能有顺序差异,调参时应同时验证结果集合重合度和 score。

最小功能验证

仓库提供的 smoke test 不依赖 embedding 或 VLM 服务:

python examples/cuvs_smoke.py # 验证 CAGRA 图索引 python examples/cuvs_smoke.py --algorithm cagra # 验证显式 float16 路径 python examples/cuvs_smoke.py --dtype float16

examples/cuvs_smoke.py 内部通过get_or_create_local_collection创建一个 4 维向量、带account_id(string)与uri(path)字段的cuvs_smokecollection,并在dense_search配置中启用backend: "cuvs"fallback_to_native: True。核心调用方式如下:

from openviking.storage.vectordb.collection.local_collection import ( get_or_create_local_collection, ) collection = get_or_create_local_collection( meta_data={ "CollectionName": "cuvs_smoke", "Fields": [ {"FieldName": "id", "FieldType": "string", "IsPrimaryKey": True}, {"FieldName": "vector", "FieldType": "vector", "Dim": 4}, {"FieldName": "account_id", "FieldType": "string"}, {"FieldName": "uri", "FieldType": "path"}, ], }, config={ "dense_search": { "backend": "cuvs", "algorithm": "brute_force", "fallback_to_native": True, } }, ) collection.create_index( "default", { "IndexName": "default", "VectorIndex": {"IndexType": "flat", "Distance": "cosine"}, "ScalarIndex": ["account_id", "uri"], }, ) collection.upsert_data( [ {"id": "a", "vector": [1, 0, 0, 0], "account_id": "demo", "uri": "/docs/a"}, {"id": "b", "vector": [0, 1, 0, 0], "account_id": "demo", "uri": "/docs/b"}, ] ) result = collection.search_by_vector( "default", dense_vector=[1, 0, 0, 0], limit=2, filters={"op": "must", "field": "account_id", "conds": ["demo"]}, ) assert [item.id for item in result.data] == ["a", "b"] collection.close()

这段代码演示了 cuVS 后端的完整闭环:collection 创建(传入dense_search配置)→ 索引声明(flat + cosine,附带标量索引)→ upsert → 带标量过滤的 dense 检索 → 断言结果顺序 → 关闭。注意检索仍走 local collection 的统一 API,cuVS 只在 dense 分支上被透明调度。

配置行为还有专门的单测覆盖(tests/vectordb/test_cuvs_config.py),大规模性能对比可参考 benchmark/cuvs/ 下的 collection、index 与服务并发 benchmark 脚本及汇总工具,以及通用的 vector backend 计时框架 benchmark/vectordb_perf/。更深入的设计背景可阅读 cuVS 集成设计文档。

当前阶段的限制

  • cuVS 只接管 pure dense search;sparse/hybrid query 在fallback_to_native=true时走原生本地索引。
  • local 集成通过 native scalar/path index 生成 prefilter,因此继承原生 DSL、date_timegeo_point和 path depth 的过滤语义,而不是在 Python 重复实现。
  • 每次 GPU rebuild 会向 native engine 注册一次 cuVS label 顺序。新过滤条件直接复用 native scalar/path index 的 bitmap,再投影为 cuVS row bitset,不再用 Python 扫描所有 host-side records。
  • filter_cache_size会保留最近使用的 GPU bitset 或 native 路由决策,并在数据更新时失效;auto 模式在进入 cuVS search 前预判候选数,不同的首次过滤条件可通过 native engine 的共享读路径并行计算,命中已缓存的 native 路由时则直接进入 native index。generation 校验会阻止跨 mutation 计算出的旧结果写入路由缓存。
  • GPU index 使用 immutable snapshot 和可复用的 cuVS resources/CUDA stream;默认关闭的 micro-batching 可让 compatible warm request 绕过 caller 侧 gate 入队,并由唯一持有 device-search gate 执行 matrix search 的 worker 合批。mutation 和 snapshot commit 使用跨后端写锁。
  • 默认情况下,每次 upsert/delete 后仍由下一次查询同步重建;开启auto_background_rebuild后,dirty 期间查询走 native,连续写被合并为后台重建。
  • cuVS 索引不作为权威持久化数据;进程重启时会从 OpenViking 本地 store 重建,因此不受 cuVS 跨版本序列化格式变化影响。
  • brute_force适合功能对齐和 ground truth;CAGRA 的 graph/search 参数需要在后续结合召回率、QPS、延迟和显存进行调优。

落地建议小结

  1. 先验证、后近似:用brute_force+dtype: float32跑通 smoke test 确认 GPU 链路,再切 CAGRA 并调build_params/search_params
  2. 显存不确定时用 auto 模式:保留backend: "local"并打开auto_enable,让 OpenViking 按显存预算自动准入,失败自动回落 native;生产上要求固定数值行为时再用显式backend: "cuvs"
  3. 调吞吐再开 micro-batching:仅在确认 GPU 显存带宽瓶颈、且 workload 并发充足时开启,并同时验证 Recall@K 与结果集合重合度。
  4. 尊重配置约束CuVSConfig使用extra: "forbid"并内建 validator,拼错字段名或违规组合(如 micro-batching + CAGRA)会在配置加载阶段直接报错,而不是静默降级。

【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询