Milvus KScaNN优化 特性指南
简介
Milvus数据库支持ScaNN索引算法。ScaNN(Scalable Nearest Neighbors)是由Google发布的高效向量相似性检索开源算法库,基于IVFPQ原理,通过x86上的4bit SIMD深度优化和各向异性量化损失函数优化,实现了极高的检索性能。然而,Milvus中支持的ScaNN算法并非源自Google,而是从Faiss中的IVFPQFastScan扩展而来。根据Ann-benchmarks提供的性能曲线显示,ScaNN算法优于Faiss-IVFPQFastScan,在高精度方面也优于Milvus-HNSW。对接Google的ScaNN算法可以显著提升查询性能。
然而,由于鲲鹏芯片的架构差异,ScaNN算法的软硬件协同优势在鲲鹏服务器上无法完全发挥,因此推出KScaNN优化特性,用于优化ScaNN类算法在鲲鹏服务器上的性能表现。KScaNN(Kunpeng Scalable Nearest Neighbors)是一种基于倒排索引,并针对鲲鹏芯片架构进行了深度优化索引布局、算法流程和计算流程的向量检索算法,旨在充分利用芯片潜力。KScaNN接口基于ScaNN开源接口进行了扩展及修改,提供了与开源ScaNN相当的完整检索能力。
此功能以patch文件的形式实现,将KScaNN算法集成到开源的Milvus数据库中,以实现对新的图索引算法的无缝支持。具体使用方法请参见安装和使用说明。
原理描述
在每次查询前,Milvus都会验证所使用的索引算法,这一过程在INDEX_NODE中进行。只有当验证通过,QUERY_NODE才会调用相应索引算法中的接口,执行查询操作。这两个节点的操作均采用Go语言实现。Milvus的整体查询结构如图 1 Milvus整体查询架构所示。

索引算法的关键组件名为Knowhere,主要采用C++实现,并会链接核心索引算法(如Faiss或HNSW等)和CGO接口进行调用。
综上所述,引入KScaNN算法,需在两个方面进行处理。
- 在INDEX_NODE中添加对KScaNN算法的验证,此处用Go语言实现;
- 在Knowhere组件中,引入对KScaNN的对接实现,此处用C++语言实现;
本文基于鲲鹏服务器和openEuler操作系统提供指导,在正式操作前请确保软硬件均满足要求。
| 项目 | 规格 |
|---|---|
| CPU | 鲲鹏920系列处理器 |
| 项目 | 版本 | 获取地址 |
|---|---|---|
| 操作系统 | openEuler 22.03 LTS SP3 | 获取链接 |
| 操作系统 | openEuler 22.03 LTS SP4 | 获取链接 |
| Milvus | 2.4.5 | 获取链接 |
| KSL | BoostKit-ksl_2.4.0.zip | 获取链接 |
| KScaNN | BoostKit-SRA_Recall-1.2.0.zip | 单击获取链接,进入“搜推广加速套件>鲲鹏召回算法库>KScaNN”,单击“软件包下载”,根据提示下载“BoostKit-SRA_Recall-1.2.0.zip”。 本特性仅使用了鲲鹏召回算法库“BoostKit-SRA_Recall-1.2.0.zip”中的KScaNN算法进行优化。 |
| 补丁文件 | 0001-milvus-add-kbest-kscann.patch | 获取链接 |
| 补丁文件 | 0001-knowhere-add-kbest-kscann.patch | 获取链接 |
针对Milvus数据库的KScaNN优化特性以patch文件的形式提供,在应用这个优化特性之前,需要先安装鲲鹏召回算法库,以确保patch文件能够顺利通过编译。
开源的Milvus源码不包含索引相关组件Knowhere,因此在编译过程中需要单独拉取Knowhere的源码并进行集成。本次优化特性主要针对索引查询,相关的补丁文件将主要添加到Knowhere的源码中。因此,整个使用流程需要对Milvus进行两次编译:第一次编译时拉取Knowhere的源码,第二次编译在应用了补丁文件后进行,以启用优化特性。
下载鲲鹏召回算法库放在主目录“~”下,解压并安装。
获取路径请参见表 2 操作系统和软件要求的KScaNN获取路径,执行以下命令解压和安装。
cd ~ unzip BoostKit-SRA_Recall-1.2.0.zip rpm -ivh boostkit-sra_recall-1.2.0-1.aarch64.rpm下载鲲鹏系统库放在主目录“~”下,解压并安装。
获取路径请参见表 2 操作系统和软件要求的KSL获取路径,执行以下命令解压和安装。
cd ~ unzip BoostKit-ksl_2.4.0.zip rpm -ivh boostkit-ksl-2.4.0-1.aarch64.rpm使用git克隆Milvus并切换到2.4.5版本,放在主目录“~”下。
获取路径请参见表 2 操作系统和软件要求,参见《Milvus 安装指南》完成Milvus的编译安装。
获取优化特性的补丁文件,将其上传到主目录“~”下。
获取路径请参见表 2 操作系统和软件要求。
执行以下命令,合入优化特性。没有输出则说明合入成功。若是编译Milvus时进行过“~/milvus/internal/core/conanfile.py”文件内容的修改,可在合入patch之后再手动添加。
cd ~/milvus git status git restore . git apply --whitespace=nowarn < ~/0001-milvus-add-kbest-kscann.patch cd ~/milvus/cmake_build/thirdparty/knowhere/knowhere-src/ git apply --whitespace=nowarn < ~/0001-knowhere-add-kbest-kscann.patch由于召回算法库仅提供KScaNN的动态库文件,因此客户需要自行生成OpenScann的动态库文件libscann_cc.so。以下给出简单操作步骤,更详细的使用说明,请参考《鲲鹏召回算法库开发指南》中的SRA_Recall使用说明。
安装依赖包。
yum install python python3-pip python3-devel java-11-openjdk java-11-openjdk-devel rsync libomp hdf5 hdf5-devel gtest-devel libuuid-devel yum install gcc-toolset-12*安装依赖软件bazel-5.3.0。
cd ~ wget https://github.com/bazelbuild/bazel/releases/download/5.3.0/bazel-5.3.0-dist.zip --no-check-certificate unzip bazel-5.3.0-dist.zip -d bazel-5.3.0 cd bazel-5.3.0 env EXTRA_BAZEL_ARGS="--tool_java_runtime_version=local_jdk" bash ./compile.sh export PATH=~/bazel-5.3.0/output:$PATH下载编译OpenScann。
export PATH=/opt/openEuler/gcc-toolset-12/root/usr/bin/:$PATH export LD_LIBRARY_PATH=/opt/openEuler/gcc-toolset-12/root/usr/lib64/:$LD_LIBRARY_PATH cd ~ wget https://gitee.com/openeuler/sra_scann_adapter/repository/archive/v1.1.0.zip --no-check-certificate unzip v1.1.0.zip -d OpenScann cd OpenScann/sra_scann_adapter-v1.1.0激活Python虚拟环境之后,编译libscann_cc.so。
鲲鹏920处理器
conda activate milvus sh project.sh -ah鲲鹏920新型号处理器
conda activate milvus sh project.sh -ag若是使用openEuler 22.03 LTS SP4的操作系统,可能使用Yum下载的gcc12编译libscann_cc.so会报lto-wrapper的错误。可以在“/etc/yum.repos.d/openEuler.repo”文件中将对应的Yum源代理的SP4修改成SP3,相当于使用openEuler 22.03 LTS SP3的Yum源代理,即可正常编译。
指定OpenScann的头文件路径和动态库文件路径。
export OPEN_SCANN_LIB=~/OpenScann/kscann/scann/libscann_cc.so export OPEN_SCANN_INCLUDE=~/OpenScann/kscann/scann/由于patch包中会读取OPEN_SCANN_INCLUDE的路径,运行目录下的一个python文件,所以路径的最后一个“/”是不能去掉的。
切换gcc12版本至gcc10.3.1版本,继续执行后续编译操作。
gcc12仅用于6进行libscann_cc.so的编译获取,在后续的其他Milvus编译中,需使用gcc10.3.1版本进行编译。
安装Milvus-KScaNN依赖环境。
安装依赖软件eigen-3.3.7。
git clone https://gitlab.com/libeigen/eigen.git cd eigen git checkout 33d0937c6bdf5ec999939fb17f2a553183d14a74 mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/eigen-3.3.7 make -sj && make install激活Python虚拟环境,安装Python依赖。
conda activate milvus pip install treelite==4.2.1 pip install tl2cgen conda install pybind11
回到安装目录下,再次全量编译Milvus,以启用优化特性。
cd ~/milvus make milvus通过ann-benchmarks gist数据集进行测试,可以得到使用加速优化特性前后的性能提升效果如图 1 优化特性使能前后性能对比所示。即采用鲲鹏召回算法KScaNN,对比ScaNN算法,将Milvus查询性能(QPS)提升30%以上。详细测试步骤请参见《Milvus ann-benchmarks 测试指导》。

在创建Milvus Collection时,需要指定向量的维度。测试工具在读取数据集时会加载该维度,而且指定的索引类型需要严格区分大小写。下面是测试工具ann_benchmarks的config.yml配置文件中所有相关配置的说明,如表 1 参数配置说明所示。
建议用户在Milvus启动并创建索引操作之后检查日志信息。若Milvus在日志中持续循环打印报错信息,表示配置参数错误,请根据报错信息进行问题定位与解决,确保查询能够正确执行。
KScaNN不提供外部接口,是基于开源ScaNN算法进行了侵入式修改,并对开源接口进行了扩展。因此,KScaNN的内部接口未实施全面的参数限制。Milvus在对接KScaNN后,同样不会限制开源接口的参数,所有调整均由客户根据实际需求自行完成,以达到更佳的性能表现。
| 参数名称 | 参数描述 | 类型范围 | 建议值 | 配置原则 |
|---|---|---|---|---|
| index_type | 测试时指定的索引类型。 | std::string,“KSCANN” | KSCANN | 无。 |
| metric_type | 测试时指定的距离度量方式。 | const char*,“L2”:欧氏距离“IP”:内积 | 无 | 数据集自带,无需手动配置。 |
| dim | 特征维度。 | int | 无 | 数据集自带,无需手动配置。 |
| n_leaves | 叶子节点数量。 | int | [2000] | 该参数影响图构建耗时和最终索引质量,过大可能会导致构建耗时过长以及搜索性能下降,过小则会影响检索精度。 |
| dims_per_block | 构图时的PQ阶段,多少维构成一个子向量块。 | int | [4] | 一般推荐使用4,根据实际情况自行调整。 |
| avq_threshold | 构图时的avq阈值。 | float | None | 该参数影响剪枝策略。对于IP数据集,一般使用0.2;L2数据集一般设置为空。 |
| soar_lambda | 控制正交性的参数,仅对IP数据集生效。 | float,大于0 | [-1] | -1表示不使用,IP数据集根据实际情况自行调整范围。 |
| overretrieve_factor | 指定过检索因子,与soar_lambda一起使用,仅对IP数据集生效。 | float,[1,2] | [-1] | -1表示不使用,IP数据集根据实际情况自行调整范围。 |
| train_thread | 构图时的线程数。 | int | [cpu核数] | 无特殊情况,设置成cpu核数大小。 |
| nprobe | 查询时的复杂query所采用的子空间个数。 | int,[1,n_leaves] | [250] | 根据实际情况自行调整。 |
| reorder | 查询时在重排前保存结果的数量。 | int,[k, 底库数量] | [900] | k是最终返回结果数量,reorder需根据实际情况自行调整。 |
| adp_threshold | 查询时的智能截断阈值。预留参数,当前版本暂未使用,保留供后续版本扩展使用。 | float,[0,0.8] | [0] | 根据实际情况自行调整。 |
| adp_refined | 查询时简单query所采用的子空间个数。预留参数,当前版本暂未使用,保留供后续版本扩展使用。 | int,[0,nprobe] | [0] | 典型值为0,但是搜推设置范围为[1,nprobe],这里设置为[0,nprobe]。根据实际情况自行调整。 |
| num_thread | 查询时的线程数。 | int,大于等于1 | [1] | 无特殊情况设置为1。 |
| batch_size | 并行自动分批时优先批次大小。 | int,大于等于1 | [1] | 无特殊情况设置为1。 |