pgvector 源码学习: 9 类型系统架构 (Type System Architecture)
pgvector 源码学习: 9 类型系统架构 (Type System Architecture)
今天系列文章一把梭了(接下来每半个小时发一篇, 敬请关注本公众号)。
本文对 pgvector 的内部类型系统架构 (type system architecture) 进行了技术深入解析,涵盖了内存布局 (memory layouts)、PostgreSQL 类型注册 (type registration)、输入/输出函数 (input/output functions)、类型修饰符 (type modifiers) 以及**类型转换系统 (cast system)**。
概述 (Overview)
pgvector 实现了四种自定义的 PostgreSQL 数据类型 (data types),每种都通过 PostgreSQL 的可扩展类型系统 (extensible type system) 进行注册。其实现遵循 PostgreSQL 的 varlena (可变长度 (variable-length)) 约定,并提供了完整的 I/O、二进制序列化 (binary serialization)、类型修改 (type modification) 和类型转换 (casting) 基础设施。
所有四种类型都通过以下方式集成到 PostgreSQL 的类型系统中:
用于 SQL 字面量 (SQL literals) 的文本输入/输出函数 (Text input/output functions) 用于协议序列化 (protocol serialization) 的二进制发送/接收函数 (Binary send/receive functions) 用于维度约束 (dimension constraints) 的类型修饰符函数 (Type modifier functions) 用于类型互操作性 (type interoperability) 的类型转换函数 (Cast functions) 确保数据完整性 (data integrity) 的验证函数 (Validation functions)
来源:src/vector.c 1-50src/halfvec.c 1-50src/sparsevec.c 1-50sql/vector.sql 1-50
内存布局与存储格式 (Memory Layout and Storage Format)
密集向量类型 (Dense Vector Types)
vector 和 halfvec 都使用一个简单的**密集数组表示 (dense array representation)**,并带有 **PostgreSQL varlena 头部 (header)**:
vector | 8 + 4×dim | |||
halfvec | 8 + 2×dim |
unused 字段保留供将来使用,并且必须始终为零。它提供对齐填充 (alignment padding) 和**扩展能力 (extension capability)**。
来源:src/vector.h 1-31src/halfvec.h 1-70
稀疏向量类型 (Sparse Vector Type)
sparsevec 类型使用压缩稀疏行 (compressed sparse row, CSR) 格式,其中索引 (indices) 存储在值 (values) 之前:
稀疏向量布局 (Sparse Vector Layout):
头部 (Header): 16 字节 ( vl_len_,dim,nnz,unused)索引数组 (Indices array): nnz × 4字节 (int32数组)值数组 (Values array): nnz × 4字节 (float数组)总大小 (Total size): 16 + 8×nnz字节
宏 SPARSEVEC_SIZE(nnz) 计算总大小,而 SPARSEVEC_VALUES(x) 通过计算偏移量越过索引数组来返回指向值数组的**指针 (pointer)**。
来源:src/sparsevec.h 1-41src/sparsevec.c 138-151
位向量类型 (Bit Vector Type)
位向量 (Bit vectors) 使用 PostgreSQL 内置的 varbit 类型(可变长度位字符串 (variable-length bit string)),并通过 binary_quantize() 函数创建,而不是拥有单独的类型定义 (type definition)。它们为每个维度 (dimension) 存储一位 (bit),从而提供了极高的**压缩率 (compression)**。
来源:src/vector.c 941-967sql/vector.sql 55-56
PostgreSQL 类型注册 (Type Registration with PostgreSQL)
注册流程 (Registration Flow)
每种类型都在 sql/vector.sql 中注册,并带有五个必需的函数:
*_in | vector_in()halfvec_in()、sparsevec_in() | |
*_out | vector_out()halfvec_out()、sparsevec_out() | |
*_typmod_in | vector_typmod_in()halfvec_typmod_in()、sparsevec_typmod_in() | |
*_recv | 反序列化二进制 (Deserialize binary) | vector_recv()halfvec_recv()、sparsevec_recv() |
*_send | 序列化二进制 (Serialize binary) | vector_send()halfvec_send()、sparsevec_send() |
所有类型都使用 STORAGE = external,以便在数据超过 PostgreSQL 的 TOAST 阈值 (TOAST threshold) 时,将其**行外存储 (out-of-line)**。
来源:sql/vector.sql 4-30sql/vector.sql 336-360sql/vector.sql 693-717
输入/输出系统 (Input/Output System)
文本格式解析 (Text Format Parsing)
向量输入 (Vector Input) (src/vector.c 165-270):
解析格式: [value1,value2,...]使用 strtof()进行**浮点数解析 (float parsing)(避免二次舍入 (double-rounding)**)使用 CheckElement()验证每个**元素 (element)**(无 NaN (非数字),无 Infinity (无穷大))使用 CheckDim()和CheckExpectedDim()验证维度计数 (dimension count)
稀疏向量输入 (Sparse Vector Input) (src/sparsevec.c 187-389):
解析格式: {index1:value1,index2:value2,...}/dimensions文本中使用**基于 1 的编号 (1-based numbering)(内部转换为基于 0 的 (0-based)**) 自动过滤零值 (zero values) 使用 qsort()和CompareIndices()对索引 (indices) 进行排序验证索引是唯一 (unique) 的、已排序 (sorted) 的且在范围内 (in-range)
半向量输入 (Half Vector Input) (src/halfvec.c 165-271):
与 vector相同的文本格式:[value1,value2,...]使用 Float4ToHalfUnchecked()将浮点数 (float) 转换为半精度 (half-precision)额外的范围检查 (range checking) 以防止半精度溢出 (overflow)
来源:src/vector.c 165-270src/sparsevec.c 187-389src/halfvec.c 165-271
二进制序列化 (Binary Serialization)
二进制发送/接收函数 (Binary send/receive functions) 使用 PostgreSQL 的 StringInfo **缓冲区 API (buffer API),并确保平台无关的字节顺序 (platform-independent byte ordering)**:
vector 的二进制格式 (Binary Format for vector):
2 字节: 维度 ( int16)2 字节: unused字段 (始终为 0)dim × 4 字节: 浮点数值 (float values)
sparsevec 的二进制格式 (Binary Format for sparsevec):
4 字节: 维度 ( int32)4 字节: nnz(非零元素数量 (number of non-zero elements)) (int32)4 字节: unused字段 (始终为 0)nnz× 4 字节: 索引 (int32[], 基于 0)nnz× 4 字节: 值 (float[])
半向量使用自定义的 pq_getmsghalf() 和 pq_sendhalf() 辅助函数来序列化 (serialize) 16 位的**半精度值 (half-precision values)**。
来源:src/vector.c 363-411src/sparsevec.c 492-565src/halfvec.c 28-55
类型修饰符与验证 (Type Modifiers and Validation)
类型修饰符 (Type modifiers) 允许用户在类型级别指定**维度约束 (dimension constraints)**(例如 vector(3)、halfvec(128)):
类型修饰符函数 (Type Modifier Functions):
vector_typmod_in() | 1 ≤ dim ≤ 16000 | ERRCODE_INVALID_PARAMETER_VALUE |
halfvec_typmod_in() | 1 ≤ dim ≤ 16000 | ERRCODE_INVALID_PARAMETER_VALUE |
sparsevec_typmod_in() | 1 ≤ dim ≤ 1,000,000,000 | ERRCODE_INVALID_PARAMETER_VALUE |
维度验证辅助函数 (Dimension Validation Helpers):
CheckDim(int dim)- 验证维度 (dimension) 是否在有效范围内CheckExpectedDim(int32 typmod, int dim)- 强制执行类型修饰符约束CheckDims(Type *a, Type *b)- 确保两个向量具有匹配的维度 (matching dimensions)
这些函数在以下过程中被调用:
输入函数 ( vector_in、halfvec_in、sparsevec_in)二进制接收 ( vector_recv、halfvec_recv、sparsevec_recv)类型转换函数 ( array_to_vector、halfvec_to_vector等)距离函数 (Distance functions)(确保操作数 (operands) 兼容)
来源:src/vector.c 72-96src/halfvec.c 72-96src/sparsevec.c 42-87src/vector.c 332-358
类型转换系统与类型转换 (Cast System and Type Conversions)
Cast Graph
类型转换类别 (Cast Categories)
**隐式转换 (IMPLICIT casts)**(自动发生,无需显式转换):
vector↔halfvecvector↔sparsevechalfvec↔sparsevecvector→real[]vector→bit(通过binary_quantize)halfvec→bit(通过binary_quantize)
**赋值转换 (ASSIGNMENT casts)(允许在赋值操作中进行,但在表达式中需要显式转换 (explicit cast)**):
int4[]→vector,halfvec,sparsevecreal[]→vector,halfvec,sparsevecdouble precision[]→vector,halfvec,sparsevecnumeric[]→vector,halfvec,sparsevechalfvec→real[]
来源:sql/vector.sql 154-170sql/vector.sql 490-512sql/vector.sql 862-886
数组转换实现 (Array Conversion Implementation)
array_to_vector() 系列函数处理从 PostgreSQL 数组 (arrays) 进行的转换:
数组到向量转换 (Array to Vector Conversion) (src/vector.c 432-501):
接受 int4[]、real[]、double precision[]、numeric[]使用 deconstruct_array()来提取元素对每个元素执行特定类型的转换 (type-specific conversion) 验证所有元素都是有限 (finite) 的(无 NaN,无 Infinity)
数组到稀疏向量 (Array to Sparse Vector) (src/sparsevec.c 672-798):
在第一次遍历 (pass) 中计算非零元素 (non-zero elements) 的数量 根据 nnz计数分配稀疏结构 (sparse structure)仅存储非零值及其索引 对 MSVC /fp:fast模式进行特殊处理,以检测 NaN/Infinity
来源:src/vector.c 432-501src/sparsevec.c 672-798src/halfvec.c 425-494
密集-稀疏转换 (Dense-Sparse Conversions)
密集到稀疏 (Dense to Sparse) (src/sparsevec.c 586-624):
第一次遍历:计算非零元素的数量 使用精确的 nnz计数 (exact nnz count) 分配稀疏结构第二次遍历:复制索引和值
稀疏到密集 (Sparse to Dense) (src/vector.c 1303-1321):
分配**密集向量 (dense vector)**(由 palloc0**初始化为零 (initialized to zeros)**)使用索引将稀疏值分散 (Scatter sparse values) 到密集数组中
半精度到稀疏 (Half to Sparse) (src/sparsevec.c 629-667):
使用 HalfIsZero()检测零半精度值 (zero half-precision values)在复制过程中使用 HalfToFloat4()将半精度转换为浮点数 (float)
来源:src/sparsevec.c 586-624src/vector.c 1303-1321src/sparsevec.c 629-667
内部辅助函数与宏 (Internal Helper Functions and Macros)
内存分配辅助函数 (Memory Allocation Helpers)
每种类型都提供一个**初始化函数 (initialization function)**,用于分配和初始化结构:
InitVector(dim) | palloc0(VECTOR_SIZE(dim))SET_VARSIZE() | src/vector.c |
InitHalfVector(dim) | palloc0(HALFVEC_SIZE(dim))SET_VARSIZE() | src/halfvec.c |
InitSparseVector(dim, nnz) | palloc0(SPARSEVEC_SIZE(nnz))SET_VARSIZE() | src/sparsevec.c |
所有函数都使用 palloc0() 进行**零初始化分配 (zero-initialized allocation)**,并使用 SET_VARSIZE() 设置 **varlena 头部 (varlena header)**。
Datum 提取宏 (Datum Extraction Macros)
// Vector type
#define DatumGetVector(x) ((Vector *) PG_DETOAST_DATUM(x))
#define PG_GETARG_VECTOR_P(x) DatumGetVector(PG_GETARG_DATUM(x))
#define PG_RETURN_VECTOR_P(x) PG_RETURN_POINTER(x) // HalfVector type
#define DatumGetHalfVector(x) ((HalfVector *) PG_DETOAST_DATUM(x))
#define PG_GETARG_HALFVEC_P(x) DatumGetHalfVector(PG_GETARG_DATUM(x))
#define PG_RETURN_HALFVEC_P(x) PG_RETURN_POINTER(x)
// SparseVector type
#define DatumGetSparseVector(x) ((SparseVector *) PG_DETOAST_DATUM(x))
#define PG_GETARG_SPARSEVEC_P(x) DatumGetSparseVector(PG_GETARG_DATUM(x))
#define PG_RETURN_SPARSEVEC_P(x) PG_RETURN_POINTER(x)
这些宏 (macros) 自动处理 PostgreSQL 的 Datum 类型 和 TOAST(**超大属性存储技术 (The Oversized-Attribute Storage Technique))解压缩 (decompression)**。
来源:src/vector.h 6-9src/halfvec.h 56-58src/sparsevec.h 7-9
稀疏向量值访问 (Sparse Vector Value Access)
由于稀疏向量值存储在索引数组之后,一个辅助函数会计算**值指针 (values pointer)**:
static inline float *
SPARSEVEC_VALUES(SparseVector * x)
{
return (float *) (((char *) x) + offsetof(SparseVector, indices) +
(x->nnz * sizeof(int32)));
}
这种指针运算 (pointer arithmetic) 跳过头部和索引,以到达值数组。
来源:src/sparsevec.h 32-36
验证函数 (Validation Functions)
所有验证函数 (validation functions) 都使用 ereport(ERROR, ...) 来引发带有相应错误码 (error codes) 的 **PostgreSQL 异常 (exceptions)**:
ERRCODE_DATA_EXCEPTION(数据异常错误码)- 无效的数据值ERRCODE_PROGRAM_LIMIT_EXCEEDED(程序限制超出错误码)- 维度限制超出ERRCODE_INVALID_PARAMETER_VALUE(无效参数值错误码)- 无效的类型修饰符
来源:src/vector.c 60-113src/halfvec.c 60-113src/sparsevec.c 29-116
半精度实用工具 (Half-Precision Utilities)
halfvec 类型使用一个单独的**实用工具层 (utility layer),用于半精度浮点数转换 (half-precision float conversions)**:
转换函数 (Conversion Functions):
Float4ToHalf(float)- 将 float32 转换为半精度 (half),带有范围检查 (range checking)Float4ToHalfUnchecked(float)- 转换时不进行溢出检查 (overflow checks)HalfToFloat4(half)- 将半精度转换为 float32HalfIsNan(half)- 检查 NaN (非数字)HalfIsInf(half)- 检查无穷大 (infinity)HalfIsZero(half)- 检查零值 (zero value)
平台调度 (Platform Dispatch):
系统使用运行时 CPU 检测 (runtime CPU detection) 来选择**优化的实现 (optimized implementations)**:
F16C 支持: 在带有 F16C 的 x86-64 上使用硬件指令 (hardware instructions) FLT16 支持: 在可用时使用编译器的原生 _Float16类型回退 (Fallback): 使用位操作 (bit manipulation) 的软件实现 (Software implementation)
来源:src/halfvec.h 38-51src/halfutils.hsrc/halfutils.c
总结 (Summary)
pgvector 的类型系统架构 (type system architecture) 展示了清晰的**关注点分离 (separation of concerns)**:
内存布局 (Memory Layout): 紧凑的 varlena结构 (varlena structures) 和特定于类型的优化 (type-specific optimizations)PostgreSQL 集成 (PostgreSQL Integration): 完整的 I/O、二进制序列化和类型修饰符支持 验证 (Validation): 在每个入口点 (entry point) 进行全面检查 (Comprehensive checking) 类型安全 (Type Safety): 维度检查 (Dimension checking) 和元素验证 (element validation) 可防止无效数据 互操作性 (Interoperability): 丰富的类型转换系统 (Rich cast system) 实现了类型之间的无缝转换 (seamless conversions)
该架构遵循 PostgreSQL 约定,同时增加了领域特定的优化 (domain-specific optimizations),例如稀疏存储 (sparse storage) 和半精度支持 (half-precision support)。所有公共 API (public APIs) 均通过SQL 函数定义 (SQL function definitions) 公开,这些定义委托给通过 PG_FUNCTION_INFO_V1 注册的 C 实现。
来源:src/vector.c 1-1322src/halfvec.c 1-1364src/sparsevec.c 1-1257sql/vector.sql 1-1000