向量分析性能測試
本文介紹云原生數據倉庫 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 |
準備工作
準備測試環境
本地安裝3.8及以上版本的Python環境。
下載適配云原生數據倉庫 AnalyticDB PostgreSQL 版的ann-benchmark測試工具到本地。下載鏈接,請參見adbpg_ann_benchmark。
執行如下語句,安裝測試工具依賴。
pip install -r requirements.txt
安裝20版本以上的Docker。具體操作,請參見Docker官方安裝指南。
執行以下語句,構建測試鏡像。
python install.py --proc 4 --algorithm adbpg
準備測試數據集
下載所需的數據集,將數據集放置于ann-benchmarks項目的data目錄下。
數據集 | 維度 | 樣本數 | 度量函數 | dataset參數 | 下載地址 |
GIST | 960 | 1,000,000 | L2相似度 | gist-960-euclidean | |
SIFT-10M | 128 | 10,000,000 | L2相似度 | sift-128-euclidean | |
SIFT-100M | 128 | 100,000,000 | L2相似度 | sift100m-128-euclidean | |
Deep | 96 | 10,000,000 | 余弦相似度 | deep-image-96-angular | |
Cohere | 768 | 1,000,000 | L2相似度 | cohere-768-euclidean | |
Dbpedia | 1536 | 1,000,000 | 余弦相似度 | dbpedia-openai-1000k-angular |
測試流程
步驟一:配置測試工具連接信息
編輯測試工具中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 | 設置緩存索引方式,取值說明:
|
pq_enable | 是否開啟PQ,取值說明:
|
pq_segments | PQ切分的segment數量,一般取向量維度 |
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