向量分析性能测试

更新时间:

本文介绍云原生数据仓库 AnalyticDB PostgreSQL 版向量分析的性能测试。

测试环境

云原生数据仓库 AnalyticDB PostgreSQL 版实例与客户端ECS应处于同一VPC中,以避免网络波动带来的误差。

AnalyticDB PostgreSQL服务端规格

引擎版本

高性能版节点规格

计算节点数量

计算节点存储空间

计算节点存储类型

v6.6.2.5

8C32G

2

1000 GB

ESSD 云盘 PL1

客户端ECS规格

CPU

内存

磁盘

16 核

32 GB

2 TB

准备工作

准备测试环境

  1. 本地安装3.8及以上版本的Python环境。

  2. 下载适配云原生数据仓库 AnalyticDB PostgreSQL 版的ann-benchmark测试工具到本地。下载链接,请参见adbpg_ann_benchmark

  3. 执行如下语句,安装测试工具依赖。

    pip install -r requirements.txt 
  4. 安装20版本以上的Docker。具体操作,请参见Docker官方安装指南

  5. 执行以下语句,构建测试镜像。

    python install.py --proc 4 --algorithm adbpg

准备测试数据集

下载所需的数据集,将数据集放置于ann-benchmarks项目的data目录下。

数据集

维度

样本数

度量函数

dataset参数

下载地址

GIST

960

1,000,000

L2相似度

gist-960-euclidean

GIST

SIFT-10M

128

10,000,000

L2相似度

sift-128-euclidean

SIFT-10M

SIFT-100M

128

100,000,000

L2相似度

sift100m-128-euclidean

SIFT-100M

Deep

96

10,000,000

余弦相似度

deep-image-96-angular

Deep

Cohere

768

1,000,000

L2相似度

cohere-768-euclidean

Cohere

Dbpedia

1536

1,000,000

余弦相似度

dbpedia-openai-1000k-angular

Dbpedia

测试流程

步骤一:配置测试工具连接信息

编辑测试工具中ann_benchmarks/algorithms/adbpg/module.py文件,根据实际情况填写配置信息:

# AnalyticDB PostgreSQL实例的内网地址。
self._host = 'gp-bp10ofhzg2z****-master.gpdb.rds.aliyuncs.com'

# AnalyticDB PostgreSQL实例的端口号。
self._port = 5432

# AnalyticDB PostgreSQL实例的数据库名称。
self._dbname = '<database_name>'

# AnalyticDB PostgreSQL实例的账号。
self._user = '<user_name>'

# AnalyticDB PostgreSQL实例的账号密码。
self._password = '<your_password>'

步骤二:配置测试参数

根据测试数据集,编辑测试工具中ann_benchmarks/algorithms/adbpg/config.yml文件。

float:
  any:
  - base_args: ['@metric']
    constructor: ADBPG
    disabled: false
    docker_tag: ann-benchmarks-adbpg
    module: ann_benchmarks.algorithms.adbpg
    name: adbpg
    run_groups:
      nopq_mmap:
        arg_groups: [{M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0, pq_segments: 120}]
        query_args: [[ {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 1}, 
        {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 5}, 
        {ef_search: 400, max_scan_points: 3200, pq_amp: 10, parallel: 10}, 
        {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 15}, 
        {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 20}, 
        {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 25}, 
        {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 30}, 
        {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 50}]]

arg_groups:创建索引的相关参数。如何创建向量索引,请参见创建向量索引

参数名

说明

M

HNSW索引的M值参数。M越大,构建越慢,构建精度越高。

efConstruction

HNSW索引用于控制搜索质量。

parallel_build

构建索引的并行度,一般设置为计算节点的CPU数量。

external_storage

设置缓存索引方式,取值说明:

  • 1:使用mmap缓存索引。

  • 0:使用shared_buffer缓存索引。

pq_enable

是否开启PQ,取值说明:

  • 1:开启PQ。

  • 0:不开启PQ。

pq_segments

PQ切分的segment数量,一般取向量维度dim/8

query_args:检索相关参数。

参数名

说明

ef_search

HNSW索引中控制搜索过程候选最近邻数量。

max_scan_points

控制索引最多检索的样本数。

pq_amp

开启PQ时的检索放大系数,在非PQ时不起作用。

parallel

检索的并发数,仅在Batch模式中生效。

在测试过程中,需要对上述参数进行微调,以保证95%的召回率。对于上述的测试数据集,云原生数据仓库 AnalyticDB PostgreSQL 版提供以下配置供参考,可根据相应的数据集选取对应的参数配置。

# for gist 960
float:
  any:
  - base_args: ['@metric']
    constructor: ADBPG
    disabled: false
    docker_tag: ann-benchmarks-adbpg
    module: ann_benchmarks.algorithms.adbpg
    name: adbpg
    run_groups:
      nopq_mmap:
        arg_groups: [{M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0, pq_segments: 120}]
        query_args: [[ {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 1}, {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 5}, {ef_search: 400, max_scan_points: 3200, pq_amp: 10, parallel: 10}, {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 15}, {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 20}, {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 25}, {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 30}, {ef_search: 100, max_scan_points: 3200, pq_amp: 10, parallel: 50}]]

# for deep 96
float:
  any:
  - base_args: ['@metric']
    constructor: ADBPG
    disabled: false
    docker_tag: ann-benchmarks-adbpg
    module: ann_benchmarks.algorithms.adbpg
    name: adbpg
    run_groups:
      nopq_mmap:
        arg_groups: [{M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0, pq_segments: 12}]
        query_args: [[ {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 1}, {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 5}, {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 10}, {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 15}, {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 20}, {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 25}, {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 30}, {ef_search: 400, max_scan_points: 1500, pq_amp: 10, parallel: 50}]]

# for cohere 768
float:
  any:
  - base_args: ['@metric']
    constructor: ADBPG
    disabled: false
    docker_tag: ann-benchmarks-adbpg
    module: ann_benchmarks.algorithms.adbpg
    name: adbpg
    run_groups:
      nopq_mmap:
        arg_groups: [{M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0, pq_segments: 96}]
        query_args: [[ {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 1}, {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 5}, {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 10}, {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 15}, {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 20}, {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 25}, {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 30}, {ef_search: 400, max_scan_points: 600, pq_amp: 10, parallel: 50}]]


# for dbpedia 1536
float:
  any:
  - base_args: ['@metric']
    constructor: ADBPG
    disabled: false
    docker_tag: ann-benchmarks-adbpg
    module: ann_benchmarks.algorithms.adbpg
    name: adbpg
    run_groups:
      nopq_mmap:
        arg_groups: [{M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0, pq_segments: 192}]
        query_args: [[ {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 1}, {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 5}, {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 10}, {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 15}, {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 20}, {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 25}, {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 30}, {ef_search: 400, max_scan_points: 425, pq_amp: 10, parallel: 50}]]

步骤三:测试检索召回率

完成上述参数配置后,执行以下命令进行召回率测试:

nohup python run.py --algorithm adbpg --dataset <数据集> --runs 1 --timeout 990000 
> annbenchmark_deep.log 2>&1 &
说明

dataset:需要替换为具体测试数据集。

等待测试结束后,执行以下命令以查看召回率结果:

python plot.py --dataset <数据集>  --recompute

输出结果示例:

0:    ADBPG(m=64, ef_construction=600, ef_search=400, max_scan_point=500, pq_amp=10)        recall: 0.963       qps: 126.200
1:   ADBPG(m=64, ef_construction=600, ef_search=400, max_scan_point=1000, pq_amp=10)        recall: 0.992       qps: 122.665

检查召回率是否符合预期,若不符合,需要调节参数并重新执行测试。

步骤四:测试检索性能

在完成召回率调整后,即可进行性能测试,方法与召回率测试类似,但在此环节中,需要打开Batch模式,以检测并发性能:

nohup python run.py --algorithm adbpg --dataset <数据集> --runs 1 --timeout 990000 --
batch > annbenchmark_deep.log 2>&1 &

等待测试运行结束,查看输出文件annbenchmark_deep.log,可以查看不同并发下的QPS、平均RT及P99 RT表现:

2023-12-20 17:31:39,297 - INFO - query using 25 parallel
worker 0 cost 9.50 s, qps 315.92, mean rt 0.00317, p99 rt 0.00951
2023-12-20 17:31:49,097 - INFO - QPS: 7653.155
2023-12-20 17:31:49,113 - INFO - query using 30 parallel
worker 0 cost 13.87 s, qps 216.36, mean rt 0.00462, p99 rt 0.04298
2023-12-20 17:32:03,260 - INFO - QPS: 6361.819
2023-12-20 17:32:03,281 - INFO - query using 50 parallel
worker 0 cost 20.78 s, qps 144.36, mean rt 0.00693, p99 rt 0.02735
2023-12-20 17:32:24,385 - INFO - QPS: 7107.920

测试结果

下文提供了不同数据集在不同向量数据库配置,不同索引构建模式下的性能表现结果,所有测试的召回率调节为大于或等于95%,测试过程中检索统一取Top10。其中不同索引构建模式说明如下:

索引构建模式

说明

适用场景

PQ + mmap

采用mmap向量索引的缓存与持久化存储,并且使用PQ量化方式压缩向量编码和加速向量计算。

1. 数据量大于100w。 2. 更新删除占比较小。 3. 内存资源不足以完全缓存所有向量。 4. 性能要求中等。

noPQ + mmap

采用mmap做向量索引的缓存与持久化存储,不使用PQ量化压缩向量编码和加速向量计算。

1. 更新删除占比较小 。 2. 内存资源足以完全缓存所有向量和索引。 3. 性能要求最好。

PQ + shared_buffer

采用PostgreSQL原生的shared_buffer机制进行向量索引的缓存,并且使用PQ量化方式压缩向量编码和加速向量计算。

1. 数据量大于100w。 2. 存在大量的删除更新操作。 3. 内存资源不足以存储所有向量。 4. 性能要求中等。

noPQ + shared_buffer

采用PostgreSQL原生的shared_buffer机制进行向量索引的缓存,不进行PQ量化压缩向量编码和加速向量计算。

1. 数据量小于100w。 2. 内存资源足够存储所有向量和索引。 3. 更新删除占比非常多。 4. 性能要求中等。

实例规格:8C32G * 2 segment

数据集:GIST L2 (960 * 100w)

索引构建模式:noPQ + mmap
  • 建索引参数:

    • M: 64

    • efConstruction: 600

    • parallel_build: 8

    • external_storage: 1

    • pq_enable: 0

  • 搜索参数:

    • ef_search: 100

    • max_scan_points: 3200

  • 测试结果:

    索引构建时间(s)

    查询并发

    QPS

    mean RT (ms)

    P99 RT (ms)

    485

    1

    396

    1

    2

    5

    1744

    2

    3

    10

    3073

    2

    4

    15

    3358

    3

    10

    20

    3511

    5

    15

    25

    3601

    6

    21

    30

    3689

    7

    25

    50

    3823

    12

    36

数据集:Deep IP (96 * 1000w)

索引构建模式:noPQ + mmap
  • 建索引参数:

    • M: 64

    • efConstruction: 600

    • parallel_build: 8

    • external_storage: 1

    • pq_enable: 0

  • 搜索参数:

    • ef_search: 400

    • max_scan_points: 1500

  • 测试结果:

    索引构建时间(s)

    查询并发

    qps

    mean rt (ms)

    p99 rt (ms)

    1778s

    1

    878

    1

    2

    5

    4344

    1

    2

    10

    7950

    1

    3

    15

    10114

    1

    4

    20

    10629

    1

    5

    25

    10858

    2

    7

    30

    11093

    2

    9

    50

    11354

    4

    16

数据集:cohere L2 (768 * 100w)

索引构建模式:noPQ + mmap
  • 建索引参数:

    • M: 64

    • efConstruction: 600

    • parallel_build: 8

    • external_storage: 1

    • pq_enable: 0

  • 搜索参数:

    • ef_search: 400

    • max_scan_points: 600

  • 测试结果:

    索引构建时间(s)

    查询并发

    qps

    mean rt (ms)

    p99 rt (ms)

    465s

    1

    561

    1

    2

    5

    2893

    1

    2

    10

    5108

    1

    3

    15

    5488

    2

    5

    20

    5969

    2

    8

    25

    6195

    3

    12

    30

    6098

    4

    19

    50

    6138

    7

    39

数据集:Dbpedia IP (1536 * 100w)

索引构建模式:noPQ + mmap
  • 建索引参数:

    • M: 64

    • efConstruction: 600

    • parallel_build: 8

    • external_storage: 1

    • pq_enable: 0

  • 搜索参数:

    • ef_search: 400

    • max_scan_points: 425

  • 测试结果:

    索引构建时间(s)

    查询并发

    qps

    mean rt (ms)

    p99 rt (ms)

    807s

    1

    453

    1

    2

    5

    1948

    1

    3

    10

    2820

    2

    4

    15

    2903

    4

    11

    20

    2860

    6

    19

    25

    2897

    7

    27

    30

    2880

    9

    34

    50

    2877

    16

    63