Daft 向量距离扩展 dvector 实战指南:基于原生 Rust 扩展实现 L2、余弦、汉明等向量距离函数
2026/9/17 12:39:31 网站建设 项目流程

Daft 向量距离扩展 dvector 实战指南:基于原生 Rust 扩展实现 L2、余弦、汉明等向量距离函数

【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft

dvector 是 Daft 官方示例仓库中提供的一个原生扩展(native extension),它以 Rust 动态库(cdylib)的形式为 Daft 数据引擎补充了 6 个向量距离标量函数,可直接嵌入 Daft 表达式 DSL 对向量列做逐行距离计算。读完本文,你将掌握 dvector 全部函数的数学定义与边界行为、向量列的数据类型约束、安装与加载方式,以及从 Python 包装层(examples/dvector/dvector/init.py)到 Rust 内核(examples/dvector/src/lib.rs、examples/dvector/src/vectors.rs)的完整实现原理。

dvector 是什么:为 Daft 定制的向量距离函数扩展

Daft 原生扩展基于 Arrow C Data Interface 这一稳定的 C ABI 构建,扩展与 Daft 内部的 arrow-rs 版本解耦,任何遵守该 ABI 的 Rust 扩展都能被 Daft 运行时通过dlopen加载(参见 docs/extensions/authoring.md)。dvector 正是这条扩展路径的一个完整落地示例:它把一组向量相似度/距离计算下沉到 Rust 侧,通过会话(Session)注册为 Daft 函数,从而在 Daft DataFrame 中直接以表达式的方式调用,全程无逐行 Python 开销。

dvector 提供的全部函数如下(与 examples/dvector/README.md 的函数表一致):

函数描述输入类型输出类型
l2_distance(a, b)欧几里得距离(L2)float 向量Float64
inner_product(a, b)负内积(遵循 pgvector 约定)float 向量Float64
cosine_distance(a, b)余弦距离(零模向量返回 null)float 向量Float64
l1_distance(a, b)曼哈顿距离(L1)float 向量Float64
hamming_distance(a, b)不同位置计数boolean 向量UInt32
jaccard_distance(a, b)1 - 交集/并集(并集为空时返回 null)boolean 向量Float64

向量列支持FixedSizeListListLargeList三种布局;浮点向量元素支持Float32Float64两种精度(布尔向量元素则为Boolean)。

向量数据类型的完整约束

三种列表布局

dvector 的两个输入参数必须是向量列,向量既可以是定长的FixedSizeList,也可以是变长的List(i32 offset)或LargeList(i64 offset)。在 src/vectors.rs 中,三种布局被统一抽象为VectorLayout枚举:

  • Fixed(i32):定长列表,每个向量占用固定元素个数;
  • Variable32(&OffsetBuffer<i32>)List的偏移缓冲;
  • Variable64(&OffsetBuffer<i64>)LargeList的偏移缓冲。

元素精度与统一视图

浮点向量元素支持Float32Float64。从源码 src/vectors.rs 可以看到,extract_float_valuesFloat64元素直接借用底层切片(零拷贝),对Float32元素则逐元素提升为f64后持有为Vec<f64>;任何其他元素类型都会返回TypeError(“unsupported element type ... expected Float32 or Float64”)。因此所有浮点距离函数内部统一以f64计算。

布尔向量(汉明、杰卡德距离)要求元素类型严格为Boolean,否则同样抛出TypeError(src/vectors.rs)。

空值(null)语义

向量列本身允许包含 null 行:只要任意一侧的向量为 null,对应输出行即为 null(见 src/vectors.rs 的空值检查与 tests/test_dvector.py 的test_l2_with_null用例)。此外:

  • cosine_distance在任一向量模为零(分母为 0)时返回 null,而不是报错或返回 NaN(src/lib.rs);
  • jaccard_distance在并集为空(两个向量所有位均为 false)时返回 null(src/lib.rs)。

维度校验

同一行内两个向量维度不一致会触发运行时错误:dimension mismatch at row {row}: {a_len} vs {b_len}(src/vectors.rs)。换言之,dvector 要求逐行配对的两个向量长度必须相同,但不同行之间的维度可以不同(例如变长列表列)。

安装与构建

dvector 是源码级示例扩展,安装前需要准备:

  • Rust 工具链(cargo / rustc),因为需要把 Cargo.toml 中声明的 Rust crate 编译为cdylib
  • Python 3.10+,以及setuptools-rust构建后端。

构建配置的关键点在 setup.py 与 pyproject.toml:

  • pyproject.toml声明setuptools+setuptools-rust作为构建系统,项目依赖daft,并通过[tool.uv.sources]把 daft 指向仓库根目录(path = "../..", editable = true);
  • setup.py通过RustExtension("dvector.libdvector", ...)将编译产物.so放到 Python 包dvector/目录内(Binding.NoBinding,因为扩展导出的是原始 C 符号而非 PyO3 绑定,strip=True压缩体积);
  • Cargo.toml 中[lib]crate-type = ["cdylib"]保证生成可被dlopen加载的动态库,并依赖daft-ext(启用arrow-57特性,与仓库使用的 arrow-rs 57 系列版本匹配)。

开发模式下推荐用 uv 以可编辑方式安装并运行测试:

# 编译 Rust cdylib 并以可编辑模式安装(含原生库) uv pip install -e . # 运行测试 pytest -v tests/

快速上手:在 Daft DataFrame 中使用 dvector

加载扩展

安装完成后,dvector 的函数并不会自动生效,需要显式加载到当前会话。Daft 的Session.load_extension接受模块、模块路径或共享库文件路径(实现见 daft/session.py),并且在进程内只会dlopen一次;函数仅在加载了该扩展的会话中可用(详见 docs/extensions/authoring.md 的 Session isolation 说明,以及 tests/test_dvector.py 中“未加载扩展即调用会报not found”的验证用例)。

import daft import dvector from dvector import l2_distance # 将扩展加载进当前活动会话 daft.load_extension(dvector)

计算 L2 距离

构造两个浮点向量列并直接以表达式调用l2_distance(示例取自 examples/dvector/README.md):

df = daft.from_pydict({ "a": [[1.0, 2.0, 3.0], [0.0, 0.0, 0.0]], "b": [[4.0, 5.0, 6.0], [1.0, 1.0, 1.0]], }) df.select(l2_distance(daft.col("a"), daft.col("b"))).collect() # ╭───────────╮ # │ result │ # │ --- │ # │ Float64 │ # ╞═══════════╡ # │ 5.196152 │ # ├╌╌╌╌╌╌╌╌╌╌╌┤ # │ 1.732051 │ # ╰───────────╯

第一行sqrt((1-4)^2 + (2-5)^2 + (3-6)^2) = sqrt(27) ≈ 5.196152,第二行sqrt(0+0+0) = 0[1,1,1]的距离为sqrt(3) ≈ 1.732051,与输出完全吻合。

Python 包装层如何映射到 Rust 函数

dvector/init.py 中每个函数都是一行薄包装:通过daft.get_function("dvector_l2_distance", a, b)之类的调用,按名称在当前会话中解析已注册的扩展函数(get_function的实现见 daft/session.py 附近)。函数名统一采用dvector_<fn>前缀,避免与其他扩展冲突(这是 docs/extensions/authoring.md 推荐的命名规范)。因此你既可以直接使用这些 Python 包装函数(带类型标注与文档字符串),也可以理解它们的本质是名称到会话内注册函数的解析。

源码级原理:六个距离函数的内核实现

扩展注册入口

src/lib.rs 定义了扩展模块:

  • #[daft_extension]宏为共享库生成 Daft 运行时查找的 C 符号(daft_module_magic);
  • impl DaftExtension for DvectorExtensioninstall(session)钩子中通过session.define_function依次注册六个函数;
  • 每个函数是一个独立结构体,由#[daft_func_batch(return_dtype = ...)]宏声明输出类型,接收ArrayRef(Arrow 数组)并返回DaftResult<ArrayRef>,数据全程以 Arrow 数组批量流动,无逐行 Python 解释开销。

浮点距离:L2 / 内积 / 余弦 / L1

四个浮点函数共用 src/vectors.rs 中的统一向量列视图FloatVectorColumn与三种批量映射器(map_float_vectorsmap_float_vectors_nullablemap_bool_vectors_*),核心逻辑如下(src/lib.rs):

  • dvector_l2_distancesqrt(sum((x-y)^2))
  • dvector_inner_product-(sum(x*y)),即负内积。这是 pgvector 的排序约定——距离越小表示内积越大,便于按“相似度优先”做升序 top-K;测试用例 tests/test_dvector.py 验证了1*4+2*5+3*6 = 32取负后输出-32.0
  • dvector_cosine_distance:一次遍历同时累加点积与两向量各自平方和,计算1 - dot/(|a|*|b|);当分母为 0(零模向量)时返回None即输出 null(src/lib.rs);
  • dvector_l1_distancesum(|x-y|)

布尔距离:汉明 / 杰卡德

两个布尔函数通过BoolVectorColumn统一视图读取布尔向量(每行切片为一个BooleanArray),逻辑为(src/lib.rs):

  • dvector_hamming_distancefilter(|x, y| x != y).count(),输出 UInt32;
  • dvector_jaccard_distance:先统计并集(任一为 true),并集为 0 时返回 null;否则统计交集(两者均为 true),输出1 - inter/union。测试用例 tests/test_dvector.py 验证了[T,T,F,F][T,F,T,F]的输出为1 - 1/3 = 2/3,以及全 false 向量输出 null。

批量映射与性能设计

src/vectors.rs 中的映射器在实现上做足了向量化基础工作:按列长度预分配Float64Builder/UInt32Builder容量、借用底层f64切片避免拷贝(F64Values::Borrowed)、行级 null 传播、维度校验前置。这意味着距离计算完全发生在 Rust 的 Arrow 数组层,天然适合 Daft 的列式执行模型——这是该扩展能够在批量 embedding 相似度打分等场景直接嵌入查询管道的原因。

测试与质量保障

dvector 的测试覆盖(tests/test_dvector.py)是一个值得参照的扩展验证模板:

  • 会话隔离sessfixture 创建独立Sessionload_extension(dvector),测试通过with sess:上下文管理器限定查询会话;
  • 数值正确性:对 L2、内积、余弦、L1、汉明、杰卡德逐一断言精确数学值(如sqrt(2)-32.01 - 1/sqrt(2)2/3);
  • 边界行为:null 行传播为 null、零模向量余弦为 null、全 false 杰卡德为 null、未加载扩展时调用报错。

运行pytest -v tests/即可在本地复现全部用例。

典型应用场景

基于以上能力,dvector 适合在 Daft 管道中直接完成以下工作:

  • 向量检索/相似度排序:对 embedding 列两两计算 L2 或余弦距离,配合 Daft 的sort/limit做 top-K 近似最近邻筛选(内积遵循 pgvector 约定,便于复用排序语义);
  • 多模态/批量打分:对图像、文本等模态产出的浮点向量批量计算距离矩阵或逐行打分;
  • 集合类相似度:对布尔向量(如标签集合的 one-hot/多热编码)使用汉明或杰卡德距离,例如文档去重、样本相似度筛查。

总结

dvector 是理解 Daft 原生扩展机制的极佳范例:它以 6 个标量函数覆盖了浮点与布尔向量两类距离计算,兼容三种列表布局与两种浮点精度,并完整实现了 null 传播、维度校验等边界语义。无论是直接把它作为向量距离函数库使用,还是对照 docs/extensions/authoring.md 将它作为编写自有 Rust 扩展的蓝本,你都可以从本仓库的 examples/dvector/ 目录中获得开箱即用的参考实现。

【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft

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

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

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

立即咨询