使用示例
本节介绍优化后hnswlib的测试使用示例。包括快速上手示例、性能测试和功能测试的使用示例,并且提供性能调优参数指导。
前提条件
请参见编译hnswlib完成hnswlib的编译。
目录说明
假设程序运行的目录为“/path/to/hnswlib”,完整的目录结构应如下所示:
├── configs // 数据集相关配置文件 ├── hnswlib // hnswlib头文件 ├── python_bindings // python接口文件 ├── include // 性能测试文件 ├── src // 性能测试头文件 ├── examples // 功能测试文件 ├── tests // 功能测试文件 ├── test // 测试文件,包含ut测试和fuzz测试 └── ALGO_PARAMS.md └── CMakeLists.txt // 功能测试CMakeLists.txt文件 └── LICENSE └── Makefile // 性能测试Makefile文件 └── MANIFEST.in └── pyproject.toml └── README.en.md └── README.md └── run.sh // 编译并执行功能测试的脚本 └── test.sh // 执行性能测试的脚本 └── setup.py └── TESTING_RECALL.md
快速上手示例
以下提供一个创建索引、插入元素、搜索、保存索引和加载索引的示例。
#include "hnswlib/hnswlib.h"
int main() {
int dim = 128;
int M = 16;
int efc = 200;
int efs = 30;
int k = 10;
int num_points = 1000;
int num_queries = 100;
string distance_type = "l2";
string data_type = "fp16";
// 生成FP32类型测试数据
std::default_random_engine engine(5678);
std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
std::vector<float> data(num_points * dim);
for (auto& val : data) {
val = distribution(engine);
}
std::vector<float> query(num_queries * dim);
for (auto& val : query) {
val = distribution(engine);
}
// 转换为FP16类型
vector<float16_t> data_fp16(num_points * dim);
for (int i = 0; i < num_points * dim; i++) {
data_fp16[i] = static_cast<float16_t>(data[i]);
}
vector<float16_t> query_fp16(num_queries * dim);
for (int i = 0; i < num_queries * dim; i++) {
query_fp16[i] = static_cast<float16_t>(query[i]);
}
// 创建距离计算空间和HNSW图
auto space = std::make_unique<L2SpaceHalf>(dim);
auto index = std::make_unique<HierarchicalNSWHalf>(space.get(), num_points, M, efc, 100, false);
// 构建索引
for (int i = 0; i < num_points; i++) {
index->addPoint(&data_fp16[i * dim], i);
}
if (num_points > 0) {
char* hnsw_raw = index->data_level0_memory_;
labeltype* hnsw_label_ptr = reinterpret_cast<labeltype*>(hnsw_raw + index->label_offset_);
}
// 搜索
index->setEf(efs);
vector<vector<pair<float, labeltype>>> results;
for (int i = 0; i < num_queries; i++) {
auto result = index->searchKnn(&query[i * dim], k);
vector<pair<float, labeltype>> result_vec;
while (!result.empty()) {
result_vec.push_back(result.top());
result.pop();
}
std::reverse(result_vec.begin(), result_vec.end());
results.push_back(result_vec);
}
// 保存索引
string index_file = "test_index_" + distance_type + "_" + data_type + ".bin";
try{
index->saveIndex(index_file);
} catch (const std::exception& e) {
std::cerr << "Failed to save index: " << e.what() << std::endl;
index = nullptr;
}
// 加载索引
auto loaded_index = std::make_unique<HierarchicalNSWHalf>(space.get(), 0, M, efc, 100, false);
loaded_index->loadIndex(index_file, space);
loaded_index->setEf(efs);
}
优化后的hnswlib性能测试示例
该示例以fashion-mnist-784-euclidean.hdf5数据集为例。
- 创建“data”文件夹,下载数据集到“data”文件夹中。
1 2 3
cd /path/to/hnswlib mkdir data && cd data wget http://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5 --no-check-certificate
- 为脚本添加运行权限。
cd /path/to/hnswlib chmod +x test.sh
- 运行测试脚本。
- FP32数据类型,使用NEON指令。
./test.sh hnswlib hnswlib_neon
- FP32数据类型,使用SVE指令。
./test.sh hnswlib hnswlib_sve
- FP16数据类型,使用NEON指令。
./test.sh hnswlib_fp16 hnswlib_fp16_neon
- FP16数据类型,使用SVE指令。
./test.sh hnswlib_fp16 hnswlib_fp16_sve
测试结果保存在“output”文件夹中,关注测试结果的qps指标与召回率。生成的测试结果如图所示。

- “/path/to/hnswlib/configs”文件夹下包含了不同数据集的配置信息,其中,index_path是索引保存路径,save_or_load是图索引构建或加载选项。
- 当save_or_load选项为save时表示构图模式,此时会将构建的图索引保存到“/path/to/hnswlib/index_path”中。
- 当save_or_load选项为load时表示加载模式,此时会从“/path/to/hnswlib/index_path”中加载图索引,直接用于后续的搜索。
- 首次运行时,在save模式和单NUMA的情况下运行性能测试脚本文件,保存图索引。
- 在后续运行中,如果不需要修改k_f、efs、efc的值,则可以在load模式和整机4NUMA下运行脚本获取性能数据。
- 如果需要修改k_f、efs、efc的值,则需要在save模式下重新构图。
- 召回率存在一定随机性,略低于0.99不影响结论。
- FP32数据类型,使用NEON指令。
性能测试参数调优指导
假设参数调优的配置文件路径为“/path/to/hnswlib/configs”,该文件夹的目录结构如下:
├── configs
├── hnswlib
├── hnswlib_fp16
└── hnswlib_fp16_fashion-mnist-784-euclidean.config
config文件的内容如下所示:
# HNSWLIB k_f = 16 efs = 30 efc = 200 metric = L2 nloop = 3 num_threads = 64 top_k = 10 batch_mode = false batch_size = 100 save_or_load = save index_path = indexes/hnsw/fashion.faiss
调优的参数如表1所示。
参数 |
含义 |
调优建议 |
|---|---|---|
k_f |
每个节点在HNSW图中的最大连接数。 |
较小的值可以减少索引的大小,但可能会降低搜索精度;较大的值可以提高搜索精度,但会增加索引的大小和构建时间。 |
efs |
在搜索时,每个节点的候选邻居数量。 |
较小的值可以加快搜索速度,但可能会降低搜索精度;较大的值可以提高搜索精度,但会增加搜索时间。 |
efc |
在构建索引时,每个节点的候选邻居数量。 |
较小的值可以加快索引构建速度,但可能会降低索引质量;较大的值可以提高索引的质量,但会增加索引构建时间。 |
在save_or_load = save模式下进行上述参数调优,构建索引时建议使用单NUMA,调优目标为保证召回率大于0.99的情况下,寻找qps最高的参数组合。
优化后的hnswlib功能测试示例
请参见兼容性验证进行功能测试。
父主题: hnswlib说明