Elasticsearch:向量相似度计算 - 可笑的速度

作者:Chris Hegarty

任何向量数据库的核心都是距离函数,它确定两个向量的接近程度。 这些距离函数在索引和搜索期间执行多次。 当合并段或在图表中导航最近邻居时,大部分执行时间都花在比较向量的相似性上。 对这些距离函数进行微观优化是值得的,我们已经从之前类似的优化中受益,例如 参见 SIMD、FMA。

随着 Lucene 和 Elasticsearch 最近对标量量化的支持,我们现在比以往任何时候都更加依赖这些距离函数的 byte 变体。 根据之前的经验,我们知道这些变体仍有显着性能改进的潜力。

目前的状况

当我们利用巴拿马向量 API 来加速 Lucene 中的距离函数时,大部分注意力都集中在 float(32 位)变体上。 我们对这些方面所取得的性能改进感到非常满意。 然而,字节(8 位)变体的改进有点令人失望 - 相信我,我们尝试过! 字节变体的根本问题是它们没有充分利用 CPU 上可用的最佳 SIMD 指令。

在 Java 中进行算术运算时,最窄的类型是 int(32位)。 JVM 自动将字节值符号扩展为 int 类型的值。 考虑这个简单的标量点积实现:

int dotProduct(byte[] a, byte[] b) {
  int res = 0;
  for (int i = 0; i < a.length; i++) {
    res += a[i] * b[i];
  }
  return res;
}

a 和 b 中的元素相乘的执行方式就好像 a 和 b 都是 int 类型一样,其值是从符号扩展为 int 的相应数组索引加载的字节值。

我们的 SIMD 化实现必须是等效的,因此我们需要小心确保在乘以大字节值时不会丢失溢出。 我们通过显式地将加载的字节值扩展为 short(16位)来做到这一点,因为我们知道所有带符号的字节值相乘时都将适合带符号的 short 而不会丢失。 然后我们在累加时需要进一步扩展为 int (32 位)。

以下是 Lucene 128 位点积代码内循环体的摘录:

ByteVector va8 = ByteVector.fromArray(ByteVector.SPECIES_64, a, i);
ByteVector vb8 = ByteVector.fromArray(ByteVector.SPECIES_64, b, i);

// process first "half" only: 16-bit multiply
Vector<Short> va16 = va8.convert(B2S, 0); // B2S Byte2Short
Vector<Short> vb16 = vb8.convert(B2S, 0);
Vector<Short> prod16 = va16.mul(vb16);

// 32-bit add - S2I Short2Int
acc = acc.add(prod16.convertShape(S2I, IntVector.SPECIES_128, 0));

可视化这一点,我们可以看到我们一次只处理 4 个元素。 例如:

这一切都很好,即使有了这些显式的扩展对话,我们也可以通过算术运算的额外数据并行性获得一些不错的速度,只是没有我们知道的那么快。 我们知道还有剩余潜力的原因是,每次拓宽都会使车道数量减半,这实际上将算术运算的数量减半。 JVM 的 C2 JIT 编译器并未优化显式扩展对话。 此外,我们仅访问数据的下半部分 - 访问下半部分以外的任何内容都不会产生良好的机器代码。 这就是我们将潜在性能 “摆在桌面上” 的地方。

目前,这已经是我们在 Java 中所能做到的最好的了。 从长远来看,Panama Vector API 和/或 C2 JIT 编译器应该为此类操作提供更好的支持,但至少目前,这已经是我们能做的最好的了。 或者是吗?

介绍(另一个)巴拿马 API - FFM

OpenJDK 的巴拿马项目有几个不同的分支,我们已经看到了巴拿马向量 API 的实际应用,但该项目的旗舰是 Foregin Function and Memory API (FFM)。 FFM API 为与 Java 运行时之外的代码和内存交互提供了较低的开销。 JVM 是一项令人惊叹的工程,它抽象了架构和平台之间的许多差异,但有时它并不总是能够做出最佳权衡,这是可以理解的。 当 JVM 无法轻易做到这一点时,FFM 可以拯救我们,如果程序员不喜欢所做的权衡,那么她可以将事情掌握在自己手中。 这就是这样一个领域,巴拿马向量 API 的权衡对于 byte 大小的向量来说并不合适。

我们已经在利用 Lucene 中的外部内存支持来协调对映射的堆外索引数据的更安全的访问。 为什么不使用外部调用支持来调用已经优化的距离计算函数? 由于我们的距离计算函数很小,并且对于我们已经知道最佳 CPU 指令集的某些部署和架构集,为什么不只编写我们想要的一小块本机代码。 然后通过外部调用API来调用它。

采用外部函数 - going foregin

Elastic Cloud 具有针对向量搜索优化的配置文件。 此配置文件针对 ARM 架构,因此让我们看看如何对此进行优化。

让我们使用一些 ARM Neon 内在函数用 C 语言编写距离函数(例如点积)。 同样,我们将重点关注循环的内部主体。 看起来是这样的:

int32x4_t acc1, acc2 // = vdupq_n_s32(0);

...
// Read into 16 x 8 bit vectors.
int8x16_t va8 = vld1q_s8((const void*)(a + i));
int8x16_t vb8 = vld1q_s8((const void*)(b + i));

int16x8_t va16 = vmull_s8(vget_low_s8(va8), vget_low_s8(vb8));
int16x8_t vb16 = vmull_s8(vget_high_s8(va8), vget_high_s8(vb8));

// Accumulate 4 x 32 bit vectors (adding adjacent 16 bit lanes).
acc1 = vpadalq_s16(acc1, va16);
acc2 = vpadalq_s16(acc2, vb16);

我们将 a 和 b 向量中的 16 个 8 位值分别加载到 va8 和 vb8 中。 然后,我们将下半部分相乘并将结果存储在 va16 中 - 该结果包含 8 个 16 位值,并且该操作隐式处理加宽。 与上半部分类似。 最后,由于我们对完整的原始 16 个值进行操作,因此使用两个累加器来存储结果会更快。 vpadalq_s16 加法和累加内在函数知道如何在累加到 4 个 32 位值时隐式扩展。 总之,我们在每个循环迭代中对所有 16 字节值进行了操作。 好的!

其反汇编非常干净并且反映了上述本质。

ldr    q2, [x1, x8]     # loads first vector data into 128-bit q2
ldr    q3, [x2, x8]     # loads second vector data into 128-bit q3
smull.8h   v4, v2, v3   # multiplies low half, result in v4
smull2.8h  v2, v2, v3   # multiplies high half, result in v2
sadalp.4s  v0, v4       # accumulates into v0
sadalp.4s  v1, v2       # accumulates into v1

ARM 上的 Neon SIMD 具有算术指令,可以提供我们想要的语义,而无需进行额外的显式扩展。 C 内联公开了这些指令,以便我们可以利用的方式使用这些指令。 对密集包含值的寄存器进行的操作比我们使用巴拿马向量 API 所做的要干净得多。

回到 Java-land

最后一个难题是 Java 中的一个小 “垫片 (shim)” 层,它使用 FFM API 链接到我们的外部代码。 我们的向量数据是堆外的,我们将其映射到 MemorySegment,并根据向量维度确定偏移量和内存地址

static int dot8s(MemorySegment a, MemorySegment b, int dims) {
  var mh$ = dot8s$MH();
  try {
    return (int)mh$.invokeExact(a, b, dims);
  } catch (Throwable ex$) {
    ...
  }
}

我们还有更多的工作要做,因为现在这是特定于平台的 Java 代码,因此我们仅在 aarch64 平台上执行它,而回退到其他平台上的替代实现。

那么它实际上比巴拿马向量代码更快吗?

性能

上述有符号字节值的点积的微观基准显示,与巴拿马向量代码相比,性能提高了大约 6 倍。 这包括外部呼叫的开销。 加速的主要原因是我们能够将完整的 128 位寄存器与值打包在一起,并对所有这些值进行操作,而无需显式移动或扩展数据。

启用标量量化的宏基准测试 SO_Dense_Vector 显示出合并时间的显着改进,大约快了 3 倍 - 该实验仅插入了用于段合并的优化点积。 我们预计搜索基准也会有所改善。

概括

Java 的最新进展,即 FFM API,允许以比以前更高性能和更简单的方式与本机代码进行互操作。 通过提供通过 FFM 调用的微优化平台特定向量距离函数,可以获得显着的性能优势。

我们期待 Elasticsearch 的未来版本,其中标量量化向量可以利用这种性能改进。 当然,我们正在大量思考这与 Lucene 甚至巴拿马向量 API 的关系,以确定如何改进这些。

原文:Vector Similarity Computations - ludicrous speed — Elastic Search Labs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/429255.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

昇腾芯片解析:华为自主研发的人工智能处理器全面分析

在当今科技发展的浪潮中&#xff0c;昇腾芯片作为一种新兴的处理器&#xff0c;正引起广泛的关注和讨论。升腾芯片究竟是由哪家公司生产的&#xff1f;这个问题一直困扰着许多人。下面小编将全面介绍、分析升腾芯片的生产商及各类参数、应用&#xff0c;以便读者对其有更全面的…

【神经网络与深度学习】时间卷积网络(TCN)

概述 时间卷积网络&#xff08;Temporal Convolutional Network&#xff0c;TCN&#xff09;是一种用于处理时序数据的深度学习模型。它基于卷积神经网络&#xff08;CNN&#xff09;的思想&#xff0c;通过卷积操作来提取和学习时序数据中的特征&#xff0c;并在一系列时序预…

MyCAT学习——在openEuler22.03中安装MyCAT2(网盘下载版)

准备工作 因为MyCAT 2基于JDK 1.8开发。也需要在虚拟机中安装JDK&#xff08;JDK官网就能下载&#xff0c;我这提供一个捷径&#xff09; jdk-8u401-linux-x64.rpmhttps://pan.baidu.com/s/1ywcDsxYOmfZONpmH9oDjfw?pwdrhel下载对应的tar安装包,以及对应的jar包 安装程序包…

C++:Vector的模拟实现

创作不易&#xff0c;感谢三连 &#xff01;&#xff01; 一&#xff0c;前言 在学习string类的时候&#xff0c;我们可能会发现遍历的话下标访问特别香&#xff0c;比迭代器用的舒服&#xff0c;但是下标其实只能是支持连续的空间&#xff0c;他的使用是非常具有局限性的&am…

开发一套小程序所需的费用取决于多个因素

随着移动互联网的发展&#xff0c;小程序已经成为许多企业和个人推广业务和服务的重要工具。 不过&#xff0c;对于很多想要开发小程序的人来说&#xff0c;最大的疑问就是开发一套小程序要花多少钱。 这个问题的答案并不是固定的&#xff0c;因为开发一个小程序的成本取决于几…

Linux 学习笔记(12)

十二、 系统服务 1 、系统服务分类&#xff0c;根据其使用的方法来分&#xff0c;可以被分为三类 a、由 init 控制的服务&#xff1a;基本都是系统级别的服务&#xff0c;运行级别这一章讲的就是这一类的服务 b、由 System V 启动脚本启动的服务&#xff1a;和我们打交道最多…

紧跟潮流,再整一个短剧搜索网站

前面一大批的转存量太大了&#xff0c;有些小伙伴用不上&#xff0c;所以整了个搜索网站&#xff0c;输入关键词搜索即可。 搜短剧 http://wjsyyx.top/sdj/ 界面依旧主打朴实无华&#xff0c;搜索一步到位。 ▼ 网站界面 ▼ 搜索结果 剩下的就都会了。 ▼ 往期推荐 【Python】…

NLP_文本数据分析_3(代码示例)

目标 了解文本数据分析的作用.掌握常用的几种文本数据分析方法. 1 文件数据分析介绍 文本数据分析的作用: 文本数据分析能够有效帮助我们理解数据语料, 快速检查出语料可能存在的问题, 并指导之后模型训练过程中一些超参数的选择. 常用的几种文本数据分析方法: 标签数量分布句…

CMU 10-414/714: Deep Learning Systems --hw0

hw0 宏观上的步骤: softmax loss: 实现softmax loss代码 概念 softmax就是将结果映射到0~1之间,且所有结果相加为1(概率形式)cross-entropy loss就是计算 p ( x ) log ⁡ q ( x ) p(x)\log {q(x)} p(x)logq(x),此值可用于衡量实际输出与期望输出的距离,进而衡量预测模…

【蓝牙协议栈】【BR/EDR】【AVDTP】音视频分布传输协议

1. AVDTP概念 AVDTP即 AUDIO/VIDEO DISTRIBUTION TRANSPORT PROTOCOL(音视频分配传输协议),主要负责 A/V stream的协商、建立及传输程序,还指定了设备之前传输A/V stream的消息格式. AVDTP的传输机制和消息格式是以 RTP为基础的。RTP由 RTP Data Transfer Protocol (RTP)和…

小迪安全31WEB 攻防-通用漏洞文件上传js 验证mimeuser.ini语言特性

#知识点&#xff1a; 1、文件上传-前端验证 2、文件上传-黑白名单 3、文件上传-user.ini 妙用 4、文件上传-PHP 语言特性 #详细点&#xff1a; 检测层面&#xff1a;前端&#xff0c;后端等 2、检测内容&#xff1a;文件头&#xff0c;完整性&#xff0c;二次渲染…

【使用imgaug库调整图像大小并修改对应的XML标签框】

使用imgaug库可以方便地进行图像增强操作&#xff0c;包括调整图像大小。以下是使用imgaug库调整图像大小并修改对应的XML标签框的示例脚本&#xff1a; 注意修改输入文件夹路径、输出文件夹路径和目标尺寸为自己内容。 input_folder "path/to/your/input_folder" …

[LeetBook]【学习日记】数组内乘积

题目 按规则计算统计结果 为了深入了解这些生物群体的生态特征&#xff0c;你们进行了大量的实地观察和数据采集。数组 arrayA 记录了各个生物群体数量数据&#xff0c;其中 arrayA[i] 表示第 i 个生物群体的数量。请返回一个数组 arrayB&#xff0c;该数组为基于数组 arrayA …

PaddleOCR基于PPOCRv4的垂类场景模型微调——手写文字识别

PaddleOCR手写文字识别 一. 项目背景二. 环境配置三. 数据构造四. 模型微调五. 串联推理六. 注意事项七. 参考文献 光学字符识别&#xff08;Optical Character Recognition, OCR&#xff09;&#xff0c;ORC是指对包含文本资料的图像文件进行分析识别处理&#xff0c;获取文字…

Linux x86_64 平台下系统调用的实现

文章目录 前言一、简介二、Defining a syscall with SYSCALL_DEFINEn()2.1 SYSCALL_METADATA2.2 __SYSCALL_DEFINEx 三、Syscall table entries四、x86_64 syscall invocation参考资料 前言 本文来自 https://lwn.net/Articles/604287/ 一、简介 系统调用&#xff08;system…

Unity 角色控制(初版)

角色控制器组件&#xff0c;当然是将组件放在角色上了。 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c1 : MonoBehaviour {// 获取角色控制器private CharacterController player;void Start(){// 加载角色控制器player …

【物联网】stm32芯片结构组成,固件库、启动过程、时钟系统、GPIO、NVIC、DMA、UART以及看门狗电路的全面详解

一、stm32的介绍 1、概述 stm32: ST&#xff1a;指意法半导体 M&#xff1a;指定微处理器 32&#xff1a;表示计算机处理器位数 与ARM关系:采用ARM推出cortex-A&#xff0c;R,M三系中的M系列&#xff0c;其架构主要基于ARMv7-M实现 ARM分成三个系列&#xff1a; Cortex-A&…

机器人工具箱学习(二)

一、机械臂及运动学 1.1 机械臂构成 机械臂多采用关节式机械结构&#xff0c;一般具有6个自由度&#xff0c;其中3个用来确定末端执行器的位置&#xff0c;另外3个则用来确定末端执行装置的方向(姿态)。   如图所示&#xff0c;一个机械臂是由一组可做相对运动的关节连接的连…

Spring中Bean的作用域、实例化方式、生命周期、循环依赖问题

Spring中Bean的作用域、实例化方式、生命周期、循环依赖问题 一、Bean的作用域1.singleton2.prototype3.其他scope值 二、Bean的实例化方式1.通过构造方法实例化2.通过简单工厂模式实例化3.通过factory-bean实例化4.通过FactoryBean接口实例化5.BeanFactory和FactoryBean的区别…

http【详解】状态码,方法,接口设计 —— RestfuI API,头部 —— headers,缓存

http 状态码 1xx 服务器收到请求 2xx 请求成功 200 成功 3xx 重定向&#xff08;目标服务器返回另一个服务器的地址&#xff0c;浏览器会自动去访问另一个服务器&#xff09; 常见应用场景&#xff1a;搜索引擎&#xff0c;短网址 301 永久重定向 &#xff08;常用于已停服的…