Vector analysis performance testing

更新时间:
复制 MD 格式

This topic walks you through running vector search benchmarks on AnalyticDB for PostgreSQL using the ann-benchmarks framework. It covers environment setup, dataset preparation, recall rate testing, and throughput testing, with reference configurations and result tables for four standard datasets.

Test environment

To eliminate network latency from benchmark results, deploy your AnalyticDB for PostgreSQL instance and the Elastic Compute Service (ECS) test client in the same virtual private cloud (VPC).

AnalyticDB for PostgreSQL specifications

Engine version Edition Node specifications Compute nodes Storage per node Storage type
V6.6.2.5 Basic Edition (formerly High-performance Edition) 8 cores, 32 GB 2 1,000 GB PL1 Enterprise SSD (ESSD)

ECS specifications

CPU Memory Disk storage
16 cores 32 GB 2 TB

Prerequisites

Before you begin, ensure that you have:

  • An AnalyticDB for PostgreSQL instance (V6.6.2.5, Basic Edition)

  • An ECS instance in the same VPC as the AnalyticDB for PostgreSQL instance

  • Python 3.8 or later installed on the ECS instance

  • Docker version later than 20 installed — see Install Docker Desktop on Linux

Set up the test environment

  1. Download the ann-benchmarks test tool: adbpg_ann_benchmark_20250430.tar.gz.

  2. Install the dependencies:

    pip install -r requirements.txt
  3. Build the test image:

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

Prepare the test dataset

Download a dataset and place it in the data directory of the ann-benchmarks project.

Dataset Dimensions Samples Metric Dataset parameter Download
GIST 960 1,000,000 L2 similarity gist-960-euclidean GIST
SIFT-10M 128 10,000,000 L2 similarity sift-128-euclidean SIFT-10M
SIFT-100M 128 100,000,000 L2 similarity sift100m-128-euclidean SIFT-100M
Deep 96 10,000,000 Cosine similarity deep-image-96-angular Deep
Cohere 768 1,000,000 L2 similarity cohere-768-euclidean Cohere
Dbpedia 1,536 1,000,000 Cosine similarity dbpedia-openai-1000k-angular Dbpedia

Run the benchmark

Step 1: Configure the connection

Edit ann_benchmarks/algorithms/adbpg/module.py and fill in your instance details:

# Internal endpoint of the AnalyticDB for PostgreSQL instance
self._host = 'gp-bp10ofhzg2z****-master.gpdb.rds.aliyuncs.com'

# Port number
self._port = 5432

# Database name
self._dbname = '<database_name>'

# Database account name
self._user = '<user_name>'

# Database account password
self._password = '<YOUR_PASSWORD>'

Step 2: Configure index and search parameters

Edit ann_benchmarks/algorithms/adbpg/config.yml with your target dataset and index mode.

Index creation parameters (arg_groups) — for details on creating a vector index, see Create a vector index.

Parameter Description
M Controls the number of bi-directional links in the Hierarchical Navigable Small World (HNSW) index. Higher values improve recall but increase index build time.
efConstruction Controls search quality during index construction. Higher values improve index quality at the cost of longer build time.
parallel_build Number of parallel workers for index construction. Set this to the number of CPU cores on your compute nodes.
external_storage Index caching policy: 1 for mmap, 0 for shared_buffer.
Important

Only AnalyticDB for PostgreSQL V6.0 supports this parameter.

pq_enable Enables product quantization (PQ): 1 to enable, 0 to disable.
pq_segments Number of PQ segments. Set this to number of dimensions / 8.

Search parameters (query_args):

Parameter Description
ef_search Number of nearest neighbors examined during HNSW index search. Higher values improve recall at the cost of higher latency.
max_scan_points Maximum number of candidate vectors scanned per query. Reducing this value increases QPS but may lower recall.
pq_amp PQ amplification factor. Has no effect when PQ is disabled.
parallel Number of concurrent queries. Applies only in batch mode.

Tuning for 95% recall rate

The test targets a recall rate of ≥95% with top-10 results. When recall falls short, adjust parameters in this order:

  1. Increase ef_search first — this is the most direct lever for recall rate and has a predictable effect.

  2. If recall is still insufficient after increasing ef_search, increase max_scan_points.

  3. Once recall meets the target, reduce max_scan_points incrementally to maximize QPS while keeping recall at ≥95%.

AnalyticDB for PostgreSQL provides the following reference configurations for each test dataset, tuned to achieve ≥95% recall:

# 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}]]

# 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}]]

# 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}]]

# 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}]]

Step 3: Test the recall rate

Run the following command to start the recall rate test:

nohup python run.py --algorithm adbpg --dataset <Dataset> --runs 1 --timeout 990000 \
    > annbenchmark_deep.log 2>&1 &

Replace <Dataset> with the dataset parameter from the table above (for example, gist-960-euclidean).

After the test completes, query the results:

python plot.py --dataset <Dataset> --recompute

Sample output:

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

If recall does not reach 95%, adjust ef_search and max_scan_points as described above and re-run the test.

Step 4: Test the search performance

Vector search benchmarks measure two distinct characteristics depending on your use case:

  • Latency (parallel: 1): Measures how fast the system responds to a single query. Use this to evaluate real-time search scenarios where low response time is critical.

  • Throughput (parallel: 5 to parallel: 50): Measures how many queries the system handles per second under concurrent load. Use this to evaluate batch or multi-user search scenarios.

After confirming that the recall rate meets your target, run the throughput test with the --batch flag:

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

After the test completes, open annbenchmark_deep.log to review queries per second (QPS), average response time (RT), and P99 RT at each concurrency level:

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

Test results

The following tables show benchmark results for four datasets on a 2-node instance (8 cores, 32 GB per node). All results are measured at ≥95% recall rate with top-10 results.

Choose an index mode

Select the index mode that matches your workload before reviewing per-dataset results:

Index mode Use when
noPQ + mmap Memory is sufficient to cache all vectors and indexes. Delivers the best query performance. Suited for workloads with few update or delete operations.
PQ + mmap Vector count exceeds 1,000,000 and memory is insufficient to cache all vectors. Accepts moderate performance for better memory efficiency. Suited for workloads with few update or delete operations.
PQ + shared_buffer Vector count exceeds 1,000,000, memory is limited, and the workload has frequent update or delete operations.
noPQ + shared_buffer Vector count is below 1,000,000, memory is sufficient to cache all vectors, and the workload has frequent update or delete operations.
Index creation time reflects the one-time cost to build the index before the instance serves queries. It does not affect query performance after the index is built, but plan for it when sizing initial data load time, especially for large datasets.

Instance specifications: 8 cores and 32 GB × 2 compute nodes

Dataset: GIST L2 (960 dimensions × 1,000,000 samples)

Index mode: noPQ + mmap

Index creation parameters: M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0

Search parameters: ef_search: 100, max_scan_points: 3200

Index creation time (s) Query concurrency QPS Average RT (ms) P99 RT (ms)
485 1 396 1 2
5 1,744 2 3
10 3,073 2 4
15 3,358 3 10
20 3,511 5 15
25 3,601 6 21
30 3,689 7 25
50 3,823 12 36

Dataset: Deep IP (96 dimensions × 10,000,000 samples)

Index mode: noPQ + mmap

Index creation parameters: M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0

Search parameters: ef_search: 400, max_scan_points: 1500

Index creation time (s) Query concurrency QPS Average RT (ms) P99 RT (ms)
1,778 1 878 1 2
5 4,344 1 2
10 7,950 1 3
15 10,114 1 4
20 10,629 1 5
25 10,858 2 7
30 11,093 2 9
50 11,354 4 16

Dataset: Cohere L2 (768 dimensions × 1,000,000 samples)

Index mode: noPQ + mmap

Index creation parameters: M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0

Search parameters: ef_search: 400, max_scan_points: 600

Index creation time (s) Query concurrency QPS Average RT (ms) P99 RT (ms)
465 1 561 1 2
5 2,893 1 2
10 5,108 1 3
15 5,488 2 5
20 5,969 2 8
25 6,195 3 12
30 6,098 4 19
50 6,138 7 39

Dataset: Dbpedia IP (1,536 dimensions × 1,000,000 samples)

Index mode: noPQ + mmap

Index creation parameters: M: 64, efConstruction: 600, parallel_build: 8, external_storage: 1, pq_enable: 0

Search parameters: ef_search: 400, max_scan_points: 425

Index creation time (s) Query concurrency QPS Average RT (ms) P99 RT (ms)
807 1 453 1 2
5 1,948 1 3
10 2,820 2 4
15 2,903 4 11
20 2,860 6 19
25 2,897 7 27
30 2,880 9 34
50 2,877 16 63

What's next