StarRocks cosine_similarity_norm 函数:面向预归一化向量的余弦相似度计算与源码实现解析

发布时间:2026/9/18 14:38:18
StarRocks cosine_similarity_norm 函数:面向预归一化向量的余弦相似度计算与源码实现解析 StarRocks cosine_similarity_norm 函数面向预归一化向量的余弦相似度计算与源码实现解析【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrockscosine_similarity_norm是 StarRocks 数学函数族中的一个向量相似度函数用于在输入向量已经完成归一化的前提下通过计算两个向量夹角的余弦值来度量其方向上的相似程度。本篇完整覆盖该函数的语法、参数、返回值语义与可复制的 SQL 实战示例并结合 StarRocks 后端BE源码深入讲解它与cosine_similarity在实现路径上的关键差异、SIMD 优化细节以及错误处理机制。读完之后你既能直接在查询中使用该函数完成向量相似度排序也能理解norm 版本更快这一结论在源码层面的确切含义。一、函数语义基于夹角的相似度度量cosine_similarity_norm通过计算两个向量夹角的余弦值来度量它们的相似性。夹角只由向量的方向决定向量的模长magnitude差异会被忽略——这正是余弦度量的核心思想。该函数有一个前提假设输入向量应当是已经归一化单位化的。如果需要在计算相似度之前先对向量做归一化应改用 cosine_similarity它会在内部完成范数计算与归一化。相似度取值范围在-1 到 1之间夹角越小余弦相似度越大向量关系夹角余弦相似度方向相同平行同向0°1相互垂直90°0方向相反平行反向180°-1这个区间约束有一个隐含前提[-1, 1]只对归一化后的单位向量严格成立。如果传入的向量并未归一化结果实际上是两个向量的点积可能超出该区间——这一点在源码实现部分会得到印证。二、语法与参数语法cosine_similarity_norm(a, b)参数说明a和b待比较的两个向量必须具有相同的维度即相同的元素个数。支持的数据类型为Arrayfloat。两个数组的元素个数必须一致否则返回错误。返回值返回一个FLOAT值范围 [-1, 1]在输入为归一化向量的前提下。如果任一输入参数为 NULL 或非法维度不匹配等将报告错误。函数注册信息在 StarRocks 的函数注册表中可以确认该函数的完整签名。gensrc/script/functions.py 中定义了三个向量相似度相关函数[10102, cosine_similarity, True, False, FLOAT, [ARRAY_FLOAT, ARRAY_FLOAT], MathFunctions::cosine_similarityTYPE_FLOAT, false], [10103, cosine_similarity_norm, True, False, FLOAT, [ARRAY_FLOAT, ARRAY_FLOAT], MathFunctions::cosine_similarityTYPE_FLOAT, true], [10104, inner_product, True, False, FLOAT, [ARRAY_FLOAT, ARRAY_FLOAT], MathFunctions::inner_productTYPE_FLOAT],注册表揭示了两个关键信息cosine_similarity_norm的函数 ID 为 10103入参为两个ARRAY_FLOAT返回FLOAT它与cosine_similarity共用同一个 C 模板函数MathFunctions::cosine_similarityTYPE_FLOAT, ...区别仅在于第二个模板参数——norm 版本为true。这正是理解两个函数性能差异的入口。三、实战示例向量相似度排序以下示例完整继承自官方文档可直接在 StarRocks 中执行。1. 建表并插入向量数据CREATE TABLE t1_similarity (id int, data arrayfloat) DISTRIBUTED BY HASH(id); INSERT INTO t1_similarity VALUES (1, arrayfloat[0.1, 0.2, 0.3]), (2, arrayfloat[0.2, 0.1, 0.3]), (3, arrayfloat[0.3, 0.2, 0.1]);2. 计算每行向量与目标数组的相似度并降序排序SELECT id, data, cosine_similarity_norm([0.1, 0.2, 0.3], data) as dist FROM t1_similarity ORDER BY dist DESC;查询结果--------------------------------- | id | data | dist | --------------------------------- | 1 | [0.1,0.2,0.3] | 0.14000002 | | 2 | [0.2,0.1,0.3] | 0.13000001 | | 3 | [0.3,0.2,0.1] | 0.10000001 | ---------------------------------注意这里查询参数是常量数组[0.1, 0.2, 0.3]表列data是逐行变化的向量。这种一个查询向量 vs 一列候选向量的用法是最典型的向量近邻检索查询形态后端为其专门提供了常量列快速路径见后文。结果解读与精度说明结果中的0.14000002、0.13000001、0.10000001这类毛边是float单精度浮点运算的正常现象。可以手工验证示例中的向量并未归一化例如第 2 行的0.2*0.1 0.1*0.2 0.3*0.3 0.13这正是两个向量的点积而非严格意义的余弦相似度。若希望得到真正的余弦值应先将向量归一化或直接使用cosine_similarity。四、norm 版本的实现真相它就是点积这是理解cosine_similarity_norm最重要的源码细节。在 BE 表达式执行层 be/src/exprs/math_functions.cpp 中定义了一个向量相似度算法枚举enum class VectorSimilarityAlgorithm { kCosineSimilarity, kNormalizedCosineSimilarity, kInnerProduct, };函数入口MathFunctions::cosine_similaritymath_functions.cpp#L1567-L1574通过模板布尔参数isNorm选择算法分支template LogicalType TYPE, bool isNorm StatusOrColumnPtr MathFunctions::cosine_similarity(FunctionContext* context, const Columns columns) { if constexpr (isNorm) { return vector_similarityTYPE, VectorSimilarityAlgorithm::kNormalizedCosineSimilarity(context, columns, cosine_similarity); } return vector_similarityTYPE, VectorSimilarityAlgorithm::kCosineSimilarity(context, columns, cosine_similarity); }关键在于vector_similarity的所有热点循环math_functions.cpp#L1260-L1377中范数累加与除法都包裹在if constexpr (algorithm VectorSimilarityAlgorithm::kCosineSimilarity)之内。对于kNormalizedCosineSimilarity分支这些代码在编译期被完全剔除最终每行只执行一个操作——累加内积后直接输出} else { out[i] sum; // kNormalizedCosineSimilarity 与 kInnerProduct 共用此路径 }从源码结构看这带来两点结论语义层面cosine_similarity_norm(a, b)的返回值就是dot(a, b)。对单位向量而言点积恰好等于夹角的余弦值因此假设输入已归一化是严格成立的——函数名中的 norm 指的不是函数内部会做归一化而是输入向量已被归一化normalized。性能层面相比cosine_similarity需要额外累加两个向量的平方和并做一次开方/除法norm 版本每元素少做两次乘加累加和一次除法向量化路径下的收益在向量维度很高如 768 维、1024 维的 Embedding时尤为明显。这与文档推荐使用场景——向量已在写入侧完成归一化——完全吻合。另外可以观察到approx_cosine_similarity函数 ID 10106复用了cosine_similarityTYPE_FLOAT, false的实现见 functions.py#L66从源码结构看approx 前缀的函数是面向向量索引近似查询场景的入口与本文讨论的精确计算函数共享同一套标量实现。五、执行路径与错误处理vector_similarity主函数math_functions.cpp#L1379-L1565在处理每批数据时依次完成以下校验与分派1. 输入合法性校验直接对应文档如果任一输入参数为 null 或非法将报告错误两列行数必须相等否则返回requires equal length arrays错误列级或元素级不允许出现 NULL返回does not support null values错误每行维度必须一致且非空否则分别返回requires equal length arrays in each row或requires non-empty arrays错误。2. 常量列快速路径当查询侧是常量数组如示例中的[0.1, 0.2, 0.3]时实现通过ConstColumn检测走vector_similarity_fixed_query分支math_functions.cpp#L1260-L1324直接以 size-1 的底层数据指针作为查询向量避免对常量列做全量物化展开这正是常量向量 × N 行候选向量这一典型查询形态的优化。3. 定长向量化路径与 AVX2 优化当所有行维度一致时走vector_similarity_fixed_dim_float分支math_functions.cpp#L1326-L1377内部使用 256 位 SIMD 指令每轮并行处理 8 个floatfor (; j 7 dim; j 8) { __m256 base_vec_data _mm256_loadu_ps(base_vec j); __m256 target_vec_data _mm256_loadu_ps(target j); __m256 mul_vec _mm256_mul_ps(base_vec_data, target_vec_data); sum_vec _mm256_add_ps(sum_vec, mul_vec); ... }在开启 AVX2 的构建中norm 路径只需乘 累加两条向量指令即可完成 8 个维度的内积非 norm 路径则还需额外维护两个平方和累加器。尾部不足 8 维的部分退化为标量循环且对极小向量还使用fast_rsqrt_nr快速逆平方根近似替代精确开方。4. 零向量行为差异非 norm 的kCosineSimilarity分支中任一向量为零向量时返回 0避免 0/0norm 分支由于不做除法零向量自然得到点积 0不会产生 NaN/Inf。单元测试cosineSimilarityNormmath_functions_test.cpp#L1996-L2009验证了该语义// cosine_similarity with isNormtrue (pre-normalized vectors) float inv_sqrt2 1.0f / std::sqrt(2.0f); auto base_col build_float_array_column({{1, 0, 0}, {inv_sqrt2, inv_sqrt2, 0}}); auto target_col build_float_array_column({{1, 0, 0}, {inv_sqrt2, 0, inv_sqrt2}}); auto result MathFunctions::cosine_similarityTYPE_FLOAT, true(ctx.get(), columns); ASSERT_FLOAT_EQ(res[0], 1.0f); // 同一单位向量 - 1 ASSERT_NEAR(res[1], 0.5f, 1e-5f); // 夹角 60° 的单位向量 - 0.5同测试文件还覆盖了常量基向量cosineSimilarityConstBase、常量目标向量cosineSimilarityConstTarget、双常量cosineSimilarityBothConst以及维度不匹配报错等分支与上文描述的执行路径一一对应见 math_functions_test.cpp#L1933-L1994。六、与 cosine_similarity 的选择指南结合文档语义与上述源码实现可以给出清晰的选择依据维度cosine_similarity_normcosine_similarity输入假设向量已在写入/预处理阶段归一化任意模长向量内部计算纯点积点积 ÷ (‖a‖·‖b‖)单行计算开销一次内积内积 两个范数平方和 开方/除法未归一化输入下的结果点积可能超出 [-1, 1]严格意义的余弦相似度对应源码算法kNormalizedCosineSimilaritykCosineSimilarity实践建议如果你管理着一张向量表例如 Embedding 表在数据写入前完成归一化是更优策略——归一化是一次性的离线开销而查询路径上每行都能省去范数计算。这也解释了为什么 StarRocks 同时提供两个函数把是否需要归一化的决定权交给数据生产者查询侧按数据形态选用对应函数。七、小结cosine_similarity_norm(a, b)接受两个同维度的Arrayfloat在输入向量已归一化的前提下返回夹角余弦值取值 [-1, 1]其本质实现是预归一化向量点积BE 源码中kNormalizedCosineSimilarity分支剔除了范数计算配合 AVX2 定长向量化与常量列快速路径是三者cosine_similarity_norm、cosine_similarity、inner_product中查询开销最低的路径输入为 NULL、维度不一致或数组为空时均会直接报错便于在数据质量环节尽早暴露问题向量未归一化时请改用 cosine_similarity或在写入侧先行归一化后再用本函数。如需继续深入可参考仓库中函数注册表 gensrc/script/functions.py、BE 实现 be/src/exprs/math_functions.cpp 与测试用例 be/test/exprs/math_functions_test.cpp以及官方文档 cosine_similarity_norm 参考页。【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询