diff --git a/internal/core/bench/CMakeLists.txt b/internal/core/bench/CMakeLists.txt index f1c5a45a6e7d6c2fc7fccfe0ced17951b9ee6841..5fe3cdfbf6989d2827a7e87dc2311924880fee03 100644 --- a/internal/core/bench/CMakeLists.txt +++ b/internal/core/bench/CMakeLists.txt @@ -23,6 +23,13 @@ set(indexbuilder_bench_srcs bench_indexbuilder.cpp ) +set(bench_parameter_tuning_flat_srcs + bench_utils/parameter_tuning_utils.h + bench_utils/sift.h + bench_utils/utils.h + parameter-tuning/bench_flat.cpp + ) + set(bench_parameter_tuning_ivfflat_srcs bench_utils/parameter_tuning_utils.h bench_utils/sift.h @@ -44,6 +51,13 @@ set(bench_parameter_tuning_ivfpq_srcs parameter-tuning/bench_ivfpq.cpp ) +set(bench_parameter_tuning_hnsw_srcs + bench_utils/parameter_tuning_utils.h + bench_utils/sift.h + bench_utils/utils.h + parameter-tuning/bench_hnsw.cpp +) + add_executable(all_bench ${bench_srcs}) target_link_libraries(all_bench milvus_segcore @@ -63,6 +77,16 @@ target_link_libraries(indexbuilder_bench target_link_libraries(indexbuilder_bench benchmark::benchmark_main) +add_executable(bench_parameter_tuning_flat ${bench_parameter_tuning_flat_srcs}) +target_link_libraries(bench_parameter_tuning_flat + milvus_segcore + milvus_indexbuilder + log + pthread + ) + +target_link_libraries(bench_parameter_tuning_flat benchmark::benchmark_main) + add_executable(bench_parameter_tuning_ivfflat ${bench_parameter_tuning_ivfflat_srcs}) target_link_libraries(bench_parameter_tuning_ivfflat milvus_segcore @@ -91,4 +115,14 @@ target_link_libraries(bench_parameter_tuning_ivfpq pthread ) -target_link_libraries(bench_parameter_tuning_ivfpq benchmark::benchmark_main) \ No newline at end of file +target_link_libraries(bench_parameter_tuning_ivfpq benchmark::benchmark_main) + +add_executable(bench_parameter_tuning_hnsw ${bench_parameter_tuning_hnsw_srcs}) +target_link_libraries(bench_parameter_tuning_hnsw + milvus_segcore + milvus_indexbuilder + log + pthread + ) + +target_link_libraries(bench_parameter_tuning_hnsw benchmark::benchmark_main) \ No newline at end of file diff --git a/internal/core/bench/bench_utils/parameter_tuning_utils.h b/internal/core/bench/bench_utils/parameter_tuning_utils.h index df76892b7a7fa14b1bfa55473d14992aa3df48cd..63bcfa0e7c967d8b18d2870ac29e0fc243e2a180 100644 --- a/internal/core/bench/bench_utils/parameter_tuning_utils.h +++ b/internal/core/bench/bench_utils/parameter_tuning_utils.h @@ -138,7 +138,7 @@ class IVFSQ8_Parameter { this->nlist = nlist; this->nprobe = nprobe; assert(nlist >= 1 && nlist <= 65536); - assert(nbits == 4 || nbits == 6 || nbits == 8 || nbits == 16); +// assert(nbits == 4 || nbits == 6 || nbits == 8 || nbits == 16); assert(nprobe >= 1 && nprobe <= nlist); std::cout << *this; } @@ -224,7 +224,7 @@ class IVFPQ_Parameter { IVFPQ_Parameter(int nlist, int nbits, int m, int d, int nprobe) : nlist{nlist}, nbits{nbits}, m{m}, d{d}, nprobe{nprobe} { assert(nlist >= 1 && nlist <= 65536); - assert(nbits == 4 || nbits == 6 || nbits == 8 || nbits == 16); + assert(nbits >= 1 && nbits <= 16); assert(d % m == 0); assert(nprobe >= 1 && nprobe <= nlist); } @@ -272,14 +272,76 @@ class IVFPQ_Parameter { this->d = d; this->nprobe = nprobe; assert(nlist >= 1 && nlist <= 65536); - assert(nbits == 4 || nbits == 6 || nbits == 8 || nbits == 16); + assert(nbits >= 1 && nbits <= 16); assert(d % m == 0); assert(nprobe >= 1 && nprobe <= nlist); std::cout << *this; } friend std::ostream& operator<<(std::ostream &os, const IVFPQ_Parameter &p) { - os << "[Benchmark] IVFSQ8_Parameter: nlist = " << p.nlist << ", nbits = " << p.nbits << ", m = " << p.m << ", nprobe = " << p.nprobe << std::endl; + os << "[Benchmark] IVFPQ_Parameter: nlist = " << p.nlist << ", nbits = " << p.nbits << ", m = " << p.m << ", nprobe = " << p.nprobe << std::endl; + return os; + } +}; + +class HNSW_Parameter { +private: + int m{-1}; // Build + int efConstruction{-1}; // Build + int ef{-1}; // Search + +public: + HNSW_Parameter() = default; + + HNSW_Parameter(int m, int efConstruction) : m{m}, efConstruction{efConstruction}, ef{ef} { + assert(m >= 4 && m <= 64); + assert(efConstruction >= 8 && efConstruction <= 512); + assert(ef <= 32768); + } + + bool IsInvalid() { + return (m == -1 && efConstruction == -1 && ef == -1); + } + + int Get_m() const { + return m; + } + + bool CheckBuildPara(int m, int efConstruction) { + bool equal = (this->m == m && this->efConstruction == efConstruction); + if (!equal) { + this->m = m; + this->efConstruction = efConstruction; + std::cout << "[Benchmark] REBUILD INDEX => A New Build Parameter is Found: m = " << m << " efConstruction = " << efConstruction << std::endl; + std::cout << *this; + } + return equal; + } + + int Get_efConstruction() const { + return efConstruction; + } + + void SetSearchPara(int ef) { + this->ef = ef; + } + + int Get_ef() const { + return ef; + } + + void Set(int m, int efConstruction, int ef) { + this->m = m; + this->efConstruction = efConstruction; + this->ef = ef; + assert(m >= 4 && m <= 64); + assert(efConstruction >= 8 && efConstruction <= 512); + assert(ef <= 32768); + std::cout << *this; + } + + friend std::ostream& operator<<(std::ostream &os, const HNSW_Parameter &p) { + os << "[Benchmark] HNSW_Parameter: m = " << p.m << ", efConstruction = " << p.efConstruction << " ef = " << p.ef << std::endl; return os; } }; @@ -547,6 +609,7 @@ ivfsq8_generate_params(const milvus::knowhere::IndexType& index_type, return std::make_tuple(type_params, index_params); } + auto ivfpq_generate_params(const milvus::knowhere::IndexType& index_type, const milvus::knowhere::MetricType& metric_type, diff --git a/internal/core/bench/bench_utils/sift.h b/internal/core/bench/bench_utils/sift.h index 866b568ce6bb4e4264025ced745ff8de8f37ee58..d49361f962810ac355d212bcd3fb98a4781e6804 100644 --- a/internal/core/bench/bench_utils/sift.h +++ b/internal/core/bench/bench_utils/sift.h @@ -37,7 +37,6 @@ const std::string sift_path = "~/Projects/sift/"; * I/O functions for fvecs and ivecs *****************************************************/ -// TODO set this one! int64_t SIFT_DIM = 128; float* @@ -122,10 +121,15 @@ std::tuple<double, double, double> train(size_t& d, size_t& nt, milvus::knowhere::VecIndexPtr& index, milvus::knowhere::Config& conf) { float* xb_data = fvecs_read(sift_path + "sift_learn.fvecs", &d, &nt); auto xt_dataset = milvus::knowhere::GenDataset(nt, d, static_cast<const void*>(xb_data)); + assert(d == 128); // Fix the conf. with dimension from file. conf[milvus::knowhere::meta::DIM] = d; + if (index->index_type() == milvus::knowhere::IndexEnum::INDEX_HNSW) { + // No Train for HNSW. + return {0, 0, 0}; + } std::vector<double> vec; { auto time_n_train = [&](size_t n) { @@ -153,13 +157,15 @@ train(size_t& d, size_t& nt, milvus::knowhere::VecIndexPtr& index, milvus::knowh // If less than 10000ms (10s), iterate 10 times (together 100s). std::cout << "[Benchmark] index->Train less than 10000ms (10s), iterate 10 times (together 10s)." << std::endl; time_n_train(10); - } else { - // If more than 10000ms (10s), iterate 2 times. - std::cout << "[Benchmark] index->Train more than 10000ms (10s), iterate 2 times." << std::endl; + } else if (vec.front() < 10000) { + // If less than 100000ms (100s), iterate 2 times. + std::cout << "[Benchmark] index->Train less than 100000ms (100s), iterate 2 times." << std::endl; time_n_train(2); + } else { + // If more than 100000ms (100s), iterate NO times. + std::cout << "[Benchmark] index->Train more than 100000ms (100s), iterate NO times." << std::endl; } } - delete[] xb_data; return Cal_Min_Max_Avg(vec); } @@ -170,10 +176,14 @@ add_points(milvus::knowhere::IndexType index_type, size_t d2; float* xb = fvecs_read(sift_path + "sift_base.fvecs", &d2, &nb); assert(d == d2 || !"dataset does not have same dimension as train set"); - auto xb_dataset = milvus::knowhere::GenDataset(nb, d, static_cast<const void*>(xb)); double duration{0}; + if (index->index_type() == milvus::knowhere::IndexEnum::INDEX_HNSW) { + // Fake Train for HNSW. + index->Train(xb_dataset, conf); + } + std::vector<double> vec; { auto time_1_add_points = [&]() { @@ -200,10 +210,15 @@ add_points(milvus::knowhere::IndexType index_type, for (size_t i = 0; i < n; i++) { // Need to re-train a index. milvus::knowhere::VecIndexPtr index_ = milvus::knowhere::VecIndexFactory::GetInstance().CreateVecIndex(index_type); - float* xb_data = fvecs_read(sift_path + "sift_learn.fvecs", &d, &nt); - auto xt_dataset = milvus::knowhere::GenDataset(nt, d, static_cast<const void*>(xb_data)); - index_->Train(xt_dataset, conf); - delete[] xb_data; + if (index_->index_type() == milvus::knowhere::IndexEnum::INDEX_HNSW) { + // Fake Train for HNSW. + index_->Train(xb_dataset, conf); + } else { + float* xb_data = fvecs_read(sift_path + "sift_learn.fvecs", &d, &nt); + auto xt_dataset = milvus::knowhere::GenDataset(nt, d, static_cast<const void*>(xb_data)); + index_->Train(xt_dataset, conf); + delete[] xb_data; + } Timer t; index_->AddWithoutIds(xb_dataset, conf); @@ -240,14 +255,15 @@ add_points(milvus::knowhere::IndexType index_type, // If less than 10000ms (10s), iterate 10 times (together 100s). std::cout << "[Benchmark] index->AddWithoutIds less than 10000ms (10s), iterate 10 times (together 10s)." << std::endl; time_n_add_points(10); - } else { - // If more than 10000ms (10s), iterate 2 times. + } else if (vec.front() < 100000) { + // If less than 100000ms (100s), iterate 2 times. std::cout << "[Benchmark] index->AddWithoutIds more than 10000ms (10s), iterate 2 times." << std::endl; time_n_add_points(2); + } else { + // If more than 100000ms (100s), iterate NO times. + std::cout << "[Benchmark] index->AddWithoutIds more than 100000ms (100s), iterate NO times." << std::endl; } - } - delete[] xb; return Cal_Min_Max_Avg(vec); } @@ -327,7 +343,7 @@ compute_recall (size_t nq, size_t k, milvus::knowhere::DatasetPtr& result, int64 void ReleaseQuery(const milvus::knowhere::DatasetPtr& xq_dataset) { const float* tensor = static_cast<const float*>(xq_dataset->Get<const void*>(milvus::knowhere::meta::TENSOR)); - free((void *) tensor); + delete[] tensor; } diff --git a/internal/core/bench/parameter-tuning/Plots.ipynb b/internal/core/bench/parameter-tuning/Plots.ipynb index b40fdd8f609ba2c11bbf42368c2f0a4c94e17909..85e1383977cda2481c2266f26c4ba5f9968f5af4 100644 --- a/internal/core/bench/parameter-tuning/Plots.ipynb +++ b/internal/core/bench/parameter-tuning/Plots.ipynb @@ -250,7 +250,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.9.6" } }, "nbformat": 4, diff --git a/internal/core/bench/parameter-tuning/Plots_Search.ipynb b/internal/core/bench/parameter-tuning/Plots_Search.ipynb index b52fae2c664bb40a3328b43a1d03afab916716f3..ab363fe44646a236067e751288a5658dab6dd09c 100644 --- a/internal/core/bench/parameter-tuning/Plots_Search.ipynb +++ b/internal/core/bench/parameter-tuning/Plots_Search.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, "id": "a0badb6e-64a1-4a52-a08e-180049c46bba", "metadata": {}, "outputs": [], @@ -15,21 +15,94 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "a153c7e9-2655-4f51-a4ac-7dc5bd21c080", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " name iterations real_time cpu_time \\\n", + "0 Build: FLAT/L2/VectorFloat 3 204864000.0 204863000.0 \n", + "1 Search: FLAT/L2/VectorFloat 3 284172000.0 283670000.0 \n", + "\n", + " time_unit bytes_per_second items_per_second label error_occurred \\\n", + "0 ns NaN NaN NaN NaN \n", + "1 ns NaN NaN NaN NaN \n", + "\n", + " error_message Index Size \n", + "0 NaN 512000000.0 \n", + "1 NaN 512000000.0 \n", + "[0.204864, 0.28417200000000004]\n" + ] + }, + { + "ename": "ValueError", + "evalue": "operands could not be broadcast together with remapped shapes [original->remapped]: (98,) and requested shape (2,)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-22-6a74160daa20>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;31m# # Plot the data using bar() method\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.35\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'time (s)'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolor\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'r'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtick_label\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnlist_nprobe\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0;31m# plt.bar(x + 0.35, recall, width=0.35, label='Recall (%)', color='g')\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.9/site-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mbar\u001b[0;34m(x, height, width, bottom, align, data, **kwargs)\u001b[0m\n\u001b[1;32m 2649\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbottom\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malign\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'center'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2650\u001b[0m data=None, **kwargs):\n\u001b[0;32m-> 2651\u001b[0;31m return gca().bar(\n\u001b[0m\u001b[1;32m 2652\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwidth\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbottom\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbottom\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malign\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0malign\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2653\u001b[0m **({\"data\": data} if data is not None else {}), **kwargs)\n", + "\u001b[0;32m~/.local/lib/python3.9/site-packages/matplotlib/__init__.py\u001b[0m in \u001b[0;36minner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1359\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1360\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1361\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msanitize_sequence\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1362\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1363\u001b[0m \u001b[0mbound\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnew_sig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.9/site-packages/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36mbar\u001b[0;34m(self, x, height, width, bottom, align, **kwargs)\u001b[0m\n\u001b[1;32m 2403\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2404\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mtick_labels\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2405\u001b[0;31m \u001b[0mtick_labels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbroadcast_to\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtick_labels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpatches\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2406\u001b[0m \u001b[0mtick_label_axis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_ticks\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtick_label_position\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2407\u001b[0m \u001b[0mtick_label_axis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_ticklabels\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtick_labels\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m<__array_function__ internals>\u001b[0m in \u001b[0;36mbroadcast_to\u001b[0;34m(*args, **kwargs)\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.9/site-packages/numpy/lib/stride_tricks.py\u001b[0m in \u001b[0;36mbroadcast_to\u001b[0;34m(array, shape, subok)\u001b[0m\n\u001b[1;32m 409\u001b[0m [1, 2, 3]])\n\u001b[1;32m 410\u001b[0m \"\"\"\n\u001b[0;32m--> 411\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_broadcast_to\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshape\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msubok\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubok\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreadonly\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 412\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 413\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.9/site-packages/numpy/lib/stride_tricks.py\u001b[0m in \u001b[0;36m_broadcast_to\u001b[0;34m(array, shape, subok, readonly)\u001b[0m\n\u001b[1;32m 346\u001b[0m 'negative')\n\u001b[1;32m 347\u001b[0m \u001b[0mextras\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 348\u001b[0;31m it = np.nditer(\n\u001b[0m\u001b[1;32m 349\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflags\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'multi_index'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'refs_ok'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'zerosize_ok'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mextras\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 350\u001b[0m op_flags=['readonly'], itershape=shape, order='C')\n", + "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with remapped shapes [original->remapped]: (98,) and requested shape (2,)" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 720x288 with 1 Axes>" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "data = pd.read_csv('~/ssh_mount/bench_parameter_tuning_flat.csv', skiprows=9)\n", + "df = pd.DataFrame(data)\n", + "df.filter(like='Search', axis=0)\n", + "print(df)\n", + "\n", + "time = list(df[\"real_time\"]) # Y0\n", + "time[:] = [x /1000.0/1000.0/1000.0 for x in time] # unit: second\n", + "print(time)\n", + "f = plt.figure()\n", + "f.set_figwidth(10)\n", + "\n", + "\n", + "# # Plot the data using bar() method\n", + "x = np.arange(len([1]))\n", + "plt.bar(x, time, width=0.35, label='time (s)', color='r', tick_label=nlist_nprobe)\n", + "# plt.bar(x + 0.35, recall, width=0.35, label='Recall (%)', color='g')\n", + "\n", + "plt.title(\"FLAT Search\")\n", + "# plt.xlabel(\"nlist, nprobe\")\n", + "# plt.ylabel(\"Y NAME\")\n", + " \n", + "# Show the plot\n", + "plt.legend()\n", + "plt.show()" + ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 37, "id": "7aa4ea33-fa4e-4185-8290-f289c8ec1341", "metadata": {}, "outputs": [ { "data": { - "image/png": "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/OUv5wtf+MJEA/p+/etfz7777nvL44MOOihnnHFGkuTSSy/N5ZdfnkMPPTTPetaz8t3vfjevec1rcv/73z9Jcve73z1f+cpXDOgLAAAAAAAAAAAAAAAAAAAAAAAAAADA7O21114577zzcv311+fxj3983va2t+W5z31u9t1335x33nkTlXHGGWfkwx/+cD796U9n7733zjHHHJMbbrhh4u2PW/bXfu3X8trXvjZvectb8nM/93PZsmVLXvWqV+W0005Lktxwww3Za6+9JtrOagzoCwAAAAAAAAAAAAAAAKxN1WzKbW025QIAAAAAAAAA7ELaCYu7xmLvvffOW97ylhx33HF50YtelIMPPjjvf//787SnPS2ttZx//vk5/PDD85jHPCZvf/vb89KXvjQ333xzrrvuunzzm9/Mfvvtl7333jsXX3xxzjzzzIm3u99+++Xmm2/ODTfckD333POW6R//+Mdzr3vdK4ccckiuv/76bNq0KZs2bcr1119/yzKf//znc9hhh607+6Z1lwAAAAAAAAAAAAAAAAAAAAAAAAAAAMBSechDHpIHP/jBee9735vTTjst73znO3P44YfngQ98YD74wQ8mSX7nd34nH/vYx/KgBz0oRx55ZP75n/85xx57bG666aYceuihecUrXpFHPOIRO7XdH/uxH8snP/nJWx631vLa1742v/7rv54kOf744/OSl7wkP/ETP5GXvexlSZKrrroqe+21V/7Tf/pP685drQf/rXrr1q3t7LPPXnQ1AAAAAAAAAAAA6Iuq2ZTbg2vmAAAAdinO3wAAAOg7564AAADQX87bYadddNFFOfTQQxddjYX7zGc+kze/+c35wz/8w4nXefOb35w73/nOecELXnC7eaOe16o6p7W2dVRZm3a0sao6taq+VlUXDE07saquqKrzutsThua9sqourapLqurxE6cCAAAAAAAAAAAAAAAAAAAAAAAAAACAVTz0oQ/Nox/96Nx8880Tr7PvvvvmOc95zlS2v8MBfZO8K8mxI6a/ubV2RHc7PUmq6gFJnpHkgd06v1tVu02lpgAAAAAAAAAAAAAAAAAAAAAAAAAAAEustbboKvTC85///Oy22+TD3j7vec/L5s2bbzd9Lc/nDgf0ba19Isk3JizvSUne11q7sbX2L0kuTfLwna4VAAAAAAAA01c1mxsAAAAAAAAA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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAFfQAAAEWCAYAAACOddJKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAAB2BklEQVR4nOzde5wkVWE3/N8ZBgW8gYhGRV00YACFBVYfjTEuGhVvATRG8QpqSIw8xjfmYvQxywZ9XmNUIomvhkQEEoIakaiRGEVBYxR1kQVBueqqKHIzaHABZTnvH1272wzdMz0zNdM1s9/v59Of6a6uOn1+XdVdp05Vnym11gAAAAAAAAAAAAAAAAAAAAAAAAAAAADTmxh3BQAAAAAAAAAAAAAAAAAAAAAAAAAAAGApMKAvAAAAAAAAAAAAAAAAAAAAAAAAAAAAjMCAvgAAAAAAAAAAAAAAAAAAAAAAAAAAADACA/oCAAAAAAAAAAAAAAAAAAAAAAAAAADACAzoCwAAAAAAAAAAAAAAAAAAAAAAAAAAACMwoC8AAAAAAAAAAAAAAAAAAAAAAAAAAACMwIC+AAAAAAAAAAAAAAAAAAAAAAAAAAAAMAID+gIAAAAAAAAAAAAAy0oppZZSfnnc9QAAAAAAAAAAAABg+TGgLwAAAAAAAAAAAABLTillQynlllLKzX23B5VSVjSDuU5Os+wepZQ7Sinv7ZvWX84dU8p+8ZBynlZKubSU8j+llAtLKQfMUOcjSylfbO5/qpTyFwPmObSU8qNSymQp5eRSys+n1O0FXX5fSim7l1LOKKXcUEr5SSnl4lLKkdPVGQAAAAAAAAAAAACWEgP6AgAAAAAAAAAAALBUPafWes++2w9HXO5lSf47yQtKKXdPkv5yknxvStmnDSnnlCTvTHLvJC9qyhzVKUleUkopU6a/NMlptdbbm8dvn5LxQyOUPc735R+TfD/Jw5Ls2uS5dsTXH9l0AxMDAAAAAAAAAAAAwEIyoC8AAAAAAAAAAAAA24xmAN2XJfk/SX6R5DnzKO4XSTbUnktqrRtmsey/pjfg7RP76rZLkmcnOXUedZqTFt+XxyQ5udb6s1rr7bXWC2qt/973Oo8rpXyplHJTKeXCUsrqvueOKqV8q5TyP6WUb5dSfrfvudWllKtLKX9aSvlRkg+UUrYrpbyxlHJVs8z5pZSH9NXlN0opVzSv9Z4BgycDAAAAAAAAAAAAwKwZ0BcAAAAAAAAAAACAbcmvJdk9yQeTfDjJy+dSSDM47FeT/EMpZcVsl6+13tK8/sv6Jv92kktrrRfOpU7z1Mr7kuS8JO8ppbywlPLQ/idKKQ9O8skkb0ly3yR/lOSMUspuzSzXpTeg8b2THJXk+FLKgX1F/FKz3MOSHJ3kD5MckeSZzTKvSLKxb/5npzfA8H7pvbdPn2MmAAAAAAAAAAAAANjCgL4AAAAAAAAAAAAALFX/Wkq5qbn964jLvDzJv9da/zvJPyc5pJRy/zm89p8m2SnJG5N8dvOgvqWUV5VSzhixjFOS/FYpZYfm8cuaaf3+qC/jDSOWO8735flJ/jPJm5N8p5SyvpTymOa5lyQ5q9Z6Vq31jlrrZ5KsS29A3tRaP1lrvar2fD7Jp5M8sa/sO5KsqbXe1gyI/Kok/6fWelmzzIW11hv75n9brfWmWuv3kpyTZOUc8gAAAAAAAAAAAADAnRjQFwAAAAAAAAAAAICl6rBa687N7bCZZi6l7JjegLOnJUmt9ctJvpfkRXN47T9Iclyt9bQkf5XknGZQ3yck+dwoBdRav5jkhiSHlVIekeSx6Q2m2+8dfRnvN2Ldxva+1Fr/u9b6hlrrvkkekGR9egMMlyQPS/L8vsGGb0rya0ke2NTjGaWU80opP26ee2aS/szX11pv7Xv8kCRXTVOdH/Xd35jknrPNAwAAAAAAAAAAAABTGdAXAAAAAAAAAAAAgG3F4UnuneT/K6X8qJTyoyQPTvLyOZQ1mWT7JKm1vi/J3yc5N8nBSU6dRTmnJnlZkpck+Y9a67VzqMt8tfm+bFFrvSHJO5I8KMl9k3w/yT/2DTa8c631HrXWt5VS7p7kjGb+B9Rad05yVpLSX+SUl/h+kkfMp44AAAAAAAAAAAAAMFsG9AUAAAAAAAAAAABgObp7KWWHvttEegPUnpTk0UlWNrcnJNm/lPLoWZb/L0n+qpTy8FLKZJKvpjdo7W1JtptFOacm+Y0kv5PklFnWYS4W9H0ppfxlKeVRpZTJUsq9krw6yZW11huT/FOS55RSnl5K2a55/dWllN2T3C3J3ZNcn+T2Usozkjxthpf7hyTHlVL2LD37lVJ2nU19AQAAAAAAAAAAAGC2DOgLAAAAAAAAAAAAwHJ0c5Jb+m4vTfKUJH9da/1R3+38JJ9Kb1Db2Xh9kv9M8oUkNyU5NsnhSS5M8tFSyvajFFJr3ZDkS0nukeTjs6zDXCz0+7JTkjPTe0++neRhSX4zSWqt309yaJI3pjdw7/eT/HGSiVrr/yR5bZIPJ/nvJC/KzO/Hu5r5P53kp0nen2THWdYXAAAAAAAAAAAAAGal1FrHXQcAAAAAAAAAAAAAAAAAAAAAAAAAAADovIlxVwAAAAAAAAAAAAAAAAAAAAAAAAAAAACWgslxVwAAAAAAAAAAAAAAGE0p5aFJvjnk6X1qrd9bzPoAAAAAAAAAAAAAwLam1FrHXQcAAAAAAAAAAAAAAAAAAAAAAAAAAADovMlxVyBJ7ne/+9UVK1aMuxoAAAAAAAAAAAAAAAAAAAAAAAAAAABs484///wbaq27DXquEwP6rlixIuvWrRt3NQAAAAAAAAAAAAAAAAAAAAAAAAAAANjGlVK+O+y5icWsCAAAAAAAAAAAAAAAAAAAAAAAAAAAACxVBvQFAAAAAAAAAAAAAAAAAAAAAAAAAACAERjQFwAAAAAAAAAAAAAAAAAAAAAAAAAAAEYwOe4KAAAAAAAAAAAAAAAAAAAAAAAAAAAAdNkvfvGLXH311bn11lvHXRVatMMOO2T33XfP9ttvP/IyBvQFAAAAAAAAAAAAAAAAAAAAAAAAAACYxtVXX5173eteWbFiRUop464OLai15sYbb8zVV1+dPfbYY+TlJhawTgAAAAAAAAAAAAAAAAAAAAAAAAAAAEverbfeml133dVgvstIKSW77rprbr311lktZ0BfAAAAAAAAAAAAAAAAAAAAAAAAAACAGRjMd/mZyzo1oC8AAAAAAAAAAAAAAAAAAAAAAAAAAACMwIC+AAAAAAAAAAAAAAAAAAAAAAAAAAAAs1FKu7cRXH311Tn00EOz55575uEPf3iOOeaY3HbbbQscdKvXvOY1WblyZfbZZ5/suOOOWblyZVauXJmPfOQjeeYzn5mbbrqp9de85ppr8uxnP3ve5fzbv/1b/vzP/7yFGhnQFwAAAAAAAAAAAAAAAAAAAAAAAACALlnkQVJhKai15rnPfW4OO+ywXHHFFbniiityyy235E/+5E9aKX/Tpk0zzvOe97wn69evz1lnnZVHPOIRWb9+fdavX5/f+q3fyllnnZWdd965lbr0e9e73pXf+Z3fmXc5z3rWs/KJT3wiGzdunHdZBvQFAAAAAAAAAAAAAAAAAAAAAAAAAADosM997nPZYYcdctRRRyVJtttuuxx//PE59dRTc/PNN+fkk0/OMcccs2X+Zz/72Tn33HOTJJ/+9Kfz+Mc/PgceeGCe//zn5+abb06SrFixIn/6p3+aAw88MG9729ty4IEHbln+iiuuuNPjmaxYsSI33HBDNmzYkF/5lV/JkUcemb322isvfvGLc/bZZ+cJT3hC9txzz3z1q19NkvzsZz/LK17xijz2sY/NAQcckI997GMDyz3jjDNyyCGHJElOPvnkHHbYYXnqU5+aFStW5G//9m/zrne9KwcccEAe97jH5cc//nGS5IQTTsg+++yT/fbbLy984QuTJKWUrF69Ov/2b/82cqZhDOgLAAAAAAAAAAAAAAAAAAAAAAAAAADQYZdcckkOOuigO027973vnRUrVuTKK68cutwNN9yQt7zlLTn77LPz9a9/PatWrcq73vWuLc/vuuuu+frXv543velNuc997pP169cnST7wgQ9sGTx4tq688sq8/vWvz6WXXppLL700//zP/5wvfvGLecc73pH/+3//b5LkrW99a5785Cfnq1/9as4555z88R//cX72s5/dqZzvfOc72WWXXXL3u999y7SLL744H/3oR/O1r30tb3rTm7LTTjvlggsuyOMf//iceuqpSZK3ve1tueCCC3LRRRflfe9735ZlV61alf/8z/+cU6Z+BvQFAAAAAAAAAAAAAAAAAAAAAAAAAABYhs4777x885vfzBOe8ISsXLkyp5xySr773e9uef4FL3jBlvuvetWr8oEPfCCbNm3Khz70obzoRS+a02vuscceefSjH52JiYnsu+++ecpTnpJSSh796Ednw4YNSZJPf/rTedvb3paVK1dm9erVufXWW/O9733vTuVcc8012W233e407eCDD8697nWv7LbbbrnPfe6T5zznOUlyp7L322+/vPjFL84//dM/ZXJycsuy97///fPDH/5wTpn6Tc48CwAAAAAAAAAAAAAAAAAAAAAAAAAAAOOyzz775CMf+cidpv30pz/Nj370ozzykY/MxRdfnDvuuGPLc7feemuSpNaapz71qTn99NMHlnuPe9xjy/3nPe95Wbt2bZ785CfnoIMOyq677jqnut797nffcn9iYmLL44mJidx+++1b6nXGGWfkkY985NBydtxxxy05ZlP2Jz/5yXzhC1/IJz7xibz1rW/NN77xjUxOTubWW2/NjjvuOKdM/SbmXQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAL5ilPeUo2btyYU089NUmyadOmvP71r88xxxyTHXfcMStWrMj69etzxx135Pvf/36++tWvJkke97jH5b/+679y5ZVXJkl+9rOf5fLLLx/4GjvssEOe/vSn59WvfnWOOuqoBc3z9Kc/PX/zN3+TWmuS5IILLrjLPHvttVc2bNgwq3I35z/44IPzl3/5l/nJT36Sm2++OUly+eWX51GPetS8625AXwAAAAAAAAAAAAAAAAAAAAAAAAAAgNmotd3bDEopOfPMM/ORj3wke+65Z3bddddMTEzkTW96U5LkCU94QvbYY4/ss88+ee1rX5sDDzwwSbLbbrvl5JNPzhFHHJH99tsvj3/843PppZcOfZ0Xv/jFmZiYyNOe9rR23qch3vzmN+cXv/hF9ttvv+y7775585vffJd57nGPe+QRj3jElsGIR7Fp06a85CUvyaMf/egccMABee1rX5udd945SXLOOefkWc961rzrXuoIK2yhrVq1qq5bt27c1QAAAAAAAAAAAAAAAAAAAAAAAAAAYNxKaa+sDoy7yfLwrW99K3vvvfe4q7HFl770pRxxxBE588wztwze24Z3vOMd+clPfpLjjjuutTLn48wzz8z555+ft7zlLfMq59prr82LXvSifPazn73Lc4PWbSnl/FrrqkFlTc6rJgAAAAAAAAAAAAAAAAAAAAAAAAAAACyqX/3VX813v/vdVss8/PDDc9VVV+Vzn/tcq+XOx+GHH54bb7xx3uV873vfyzvf+c4WamRAXwAAAAAAAAAAAAAAAAAAAAAAAAAAgG3emWeeOe4qDPSqV71q3mU85jGPaaEmPRMzzVBKOamUcl0p5eK+aR8qpaxvbhtKKeub6StKKbf0Pfe+1moKAAAAAAAAAAAAAAAAAAAAAAAAAAAAYzQ5wjwnJ/nbJKdunlBrfcHm+6WUdyb5Sd/8V9VaV7ZUPwAAAAAAAAAAAAAAAAAAAAAAAAAAAOiEGQf0rbV+oZSyYtBzpZSS5LeTPLnlegEAAAAAAAAAAAAAAAAAAAAAAAAAAECnTMxz+ScmubbWekXftD1KKReUUj5fSnnisAVLKUeXUtaVUtZdf/3186wGAAAAAAAAAAAAAAAAAAAAAAAAAAAALKzJeS5/RJLT+x5fk+ShtdYbSykHJfnXUsq+tdafTl2w1npikhOTZNWqVXWe9QAAAAAAAAAAAAAAAAAAAAAAAAAAAFgUZW1ptby6xvCsS8XEXBcspUwmeW6SD22eVmu9rdZ6Y3P//CRXJdlrvpUEAAAAAAAAAAAAAAAAAAAAAAAAAADYlm233XZZuXJlHvWoR+U5z3lObrrpplbLX7FiRW644YYkyT3vec+B89xyyy150pOelE2bNuWyyy7LQQcdlP322y9f/vKXkyS33357fuM3fiMbN27csswLX/jCXHHFFa3WdZzmPKBvkt9Icmmt9erNE0opu5VStmvuPzzJnkm+Pb8qAgAAAAAAAAAAAAAAAAAAAAAAAAAAbNt23HHHrF+/PhdffHHue9/75j3vec+i1+Gkk07Kc5/73Gy33Xb5u7/7u7z73e/OWWedlXe84x1Jkve+9715yUtekp122mnLMq9+9avz9re/fdHrulBmHNC3lHJ6ki8neWQp5epSyiubp16Y5PQps/96kotKKeuTfCTJ79Vaf9xifQEAAAAAAAAAAAAAAAAAAAAAAAAAALZpj3/84/ODH/wgSXLVVVflkEMOyUEHHZQnPvGJufTSS5Mk1157bQ4//PDsv//+2X///fOlL30pSXLYYYfloIMOyr777psTTzxxVq972mmn5dBDD02SbL/99tm4cWM2btyY7bffPjfddFM+8YlP5GUve9mdlnniE5+Ys88+O7fffvt8Y3fC5Ewz1FqPGDL9yAHTzkhyxvyrBQAAAAAAAAAAAAAAAAAAAAAAAAAAwFSbNm3KZz/72bzyla9Mkhx99NF53/velz333DNf+cpX8vu///v53Oc+l9e+9rV50pOelDPPPDObNm3KzTffnCQ56aSTct/73je33HJLHvOYx+R5z3tedt111xlf9+c//3m+/e1vZ8WKFUmS17zmNXnZy16W2267LX/3d3+X4447Lm984xszMTFxp+UmJibyy7/8y7nwwgtz0EEHtftmjMGMA/oCAAAAAAAAAAAAAAAAAAAAAAAAAAAwXrfccktWrlyZH/zgB9l7773z1Kc+NTfffHO+9KUv5fnPf/6W+W677bYkyec+97mceuqpSZLtttsu97nPfZIkJ5xwQs4888wkyfe///1cccUVIw3oe8MNN2TnnXfe8vihD31ozj333CTJlVdemauvvjp77713XvrSl+bnP/95jjvuuOy1115Jkvvf//754Q9/aEBfAAAAAAAAAAAAAAAAAAAAAAAAAAAAFt6OO+6Y9evXZ+PGjXn605+e97znPTnyyCOz8847Z/369SOVce655+bss8/Ol7/85ey0005ZvXp1br311pFff9i8b3rTm/KWt7wlJ5xwQl71qldlxYoVeeMb35jTTjstSXLrrbdmxx13HOl1us6AvgAAAAAAAAAAAAAAAAAAAADA8lZKO+XU2k45AAAAwJJX14yvn2CnnXbKCSeckMMOOyy///u/nz322CP/8i//kuc///mpteaiiy7K/vvvn6c85Sl573vfm9e97nXZtGlTbr755vzkJz/JLrvskp122imXXnppzjvvvJFfd5dddsmmTZty6623Zocddtgy/fOf/3we9KAHZc8998zGjRszMTGRiYmJbNy4ccs8l19+eR71qEe1+j6My8S4KwAAAAAAAAAAAAAAAAAAAAAAAAAAAMDoDjjggOy33345/fTTc9ppp+X9739/9t9//+y777752Mc+liR597vfnXPOOSePfvSjc9BBB+Wb3/xmDjnkkNx+++3Ze++984Y3vCGPe9zjZvW6T3va0/LFL35xy+Naa97ylrfkzW9+c5Lk6KOPzh/8wR/kWc96Vv7oj/4oSXLttddmxx13zC/90i+1lH68Su3Af31atWpVXbdu3birAQAAAAAAAAAAAAAAAAAAAAAsR6W0U04HxmoBAADYJrR1HJc4lqM13/rWt7L33nuPuxpj9/Wvfz3HH398/vEf/3HkZY4//vjc+973zitf+coFrNncDVq3pZTza62rBs0/sSi1AgAAAAAAAAAAAAAAAAAAAAAAAAAAYEk78MADc/DBB2fTpk0jL7Pzzjvn5S9/+QLWanFNjrsCAAAAAAAAAAAAAAAAAAAAAAAAAAAAXVdrTSll3NUYu1e84hWzmv+oo45aoJrMX6111stMLEA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAlo0ddtghN95445wGgKWbaq258cYbs8MOO8xquckFqg8AAAAAAAAAAAAAAAAAAAAAAAAAAMCysPvuu+fqq6/O9ddfP+6q0KIddtghu++++6yWMaAvAAAAAAAAAAAAAAAAAAAAAAAAAADANLbffvvsscce464GHTAx7goAAAAA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\n", "text/plain": [ "<Figure size 7200x288 with 1 Axes>" ] @@ -38,10 +111,21 @@ "needs_background": "light" }, "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "<Figure size 432x288 with 0 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "data = pd.read_csv('~/Desktop/bench_parameter_tuning_ivfflat_v2.csv', skiprows=10)\n", + "# data = pd.read_csv('~/ssh_mount/bench_parameter_tuning_ivfflat_v2.csv', skiprows=10)\n", + "data = pd.read_csv('~/ssh_mount/bench_parameter_tuning_ivfflat_0511.csv', skiprows=9)\n", + "# data = pd.read_csv('~/Desktop/bench_parameter_tuning_ivfflat_v2.csv', skiprows=10)\n", "df = pd.DataFrame(data)\n", "df.filter(like='Search', axis=0)\n", "\n", @@ -51,28 +135,31 @@ "nlist_nprobe[:] = [str(x) + \",\" + str(nprobe[index]) for index,x in enumerate(nlist_nprobe)]\n", "\n", "time = list(df[\"real_time\"]) # Y0\n", - "time[:] = [x /1000.0/1000.0/1000.0 for x in time] # unit: second\n", + "time[:] = [x /1000.0 for x in time] # unit: second\n", "recall = list(df[\"Recall\"]) # Y1\n", "recall[:] = [x * 100 for x in recall] # unit: MiB\n", " \n", " \n", "f = plt.figure()\n", "f.set_figwidth(100)\n", - "plt.axhline(y = 100, color = 'blue', linestyle = '-')\n", + "# plt.axhline(y = 100, color = 'blue', linestyle = '-')\n", "plt.axhline(y = 95, color = 'black', linestyle = '-')\n", + "plt.axhline(y = 145, color = 'green', linestyle = '-')\n", "\n", "# Plot the data using bar() method\n", "x = np.arange(len(nlist))\n", - "plt.bar(x, time, width=0.35, label='time (s)', color='r', tick_label=nlist_nprobe)\n", + "plt.bar(x, time, width=0.35, label='Query Time (ms)', color='r', tick_label=nlist_nprobe)\n", "plt.bar(x + 0.35, recall, width=0.35, label='Recall (%)', color='g')\n", "\n", - "plt.title(\"IVF_FLAT Search\")\n", + "plt.title(\"FLAT & IVF_FLAT Search\")\n", "plt.xlabel(\"nlist, nprobe\")\n", "# plt.ylabel(\"Y NAME\")\n", " \n", "# Show the plot\n", "plt.legend()\n", - "plt.show()" + "plt.show()\n", + "plt.savefig(\"0728_ti_gexi.jpg\")\n", + "\n" ] }, { @@ -108,7 +195,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.9.6" } }, "nbformat": 4, diff --git a/internal/core/bench/parameter-tuning/TODO_TUN_GET_CSV.sh b/internal/core/bench/parameter-tuning/TODO_TUN_GET_CSV.sh new file mode 100755 index 0000000000000000000000000000000000000000..e6deca3b9058ed85f4b2d58821553346f6d983a5 --- /dev/null +++ b/internal/core/bench/parameter-tuning/TODO_TUN_GET_CSV.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +~/Projects/milvus/internal/core/cmake-build-release/bench/bench_parameter_tuning_ivfflat --benchmark_counters_tabular=true --benchmark_out=~/Projects/bench_parameter_tuning_ivfflat_0511.csv --benchmark_out_format=csv + +~/Projects/milvus/internal/core/cmake-build-release/bench/bench_parameter_tuning_ivfsq8 --benchmark_counters_tabular=true --benchmark_out=~/Projects/bench_parameter_tuning_ivfsq8_0511.csv --benchmark_out_format=csv + +~/Projects/milvus/internal/core/cmake-build-release/bench/bench_parameter_tuning_ivfpq --benchmark_counters_tabular=true --benchmark_out=~/Projects/bench_parameter_tuning_ivfpq_0610.csv --benchmark_out_format=csv + +# TODO: a speedup for google benchmark +~/Projects/milvus/internal/core/cmake-build-release/bench/bench_parameter_tuning_hnsw --benchmark_counters_tabular=true --benchmark_out=~/Projects/bench_parameter_tuning_hnsw_0618.csv --benchmark_out_format=csv diff --git a/internal/core/bench/parameter-tuning/bench_flat.cpp b/internal/core/bench/parameter-tuning/bench_flat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23a19df4d4f21488a2d622cdb25deca7b801b81d --- /dev/null +++ b/internal/core/bench/parameter-tuning/bench_flat.cpp @@ -0,0 +1,106 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under the License + +#include <benchmark/benchmark.h> + +#include "index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.h" +#include "index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.h" +#include "indexbuilder/IndexWrapper.h" +#include "indexbuilder/index_c.h" +#include "indexbuilder/utils.h" +#include "test_utils/indexbuilder_test_utils.h" +#include "bench_utils/parameter_tuning_utils.h" +#include "bench_utils/sift.h" + +namespace knowhere = milvus::knowhere; + +//auto metric_type_collections = [] { +// static std::unordered_map<int64_t, milvus::knowhere::MetricType> collections{ +// {0, milvus::knowhere::Metric::L2}, +// }; +// return collections; +//}(); + +//// auto is_binary = state.range(2); +//auto is_binary = false; +//auto dataset = GenDataset(NB, metric_type, is_binary); + +class Flat_Fixture_SIFT1M : public benchmark::Fixture { + public: + const knowhere::IndexType index_type = knowhere::IndexEnum::INDEX_FAISS_IDMAP; + knowhere::MetricType metric_type = knowhere::Metric::L2; + knowhere::VecIndexPtr index = knowhere::VecIndexFactory::GetInstance().CreateVecIndex(index_type); + IVFFLAT_Parameter para; + knowhere::Config conf = knowhere::Config{ + {knowhere::meta::DIM, 128}, + {knowhere::meta::TOPK, 100}, + {knowhere::Metric::TYPE, metric_type}, + {knowhere::INDEX_FILE_SLICE_SIZE_IN_MEGABYTE, 4}, + }; + + size_t d; + size_t nt; + size_t nb; + size_t nq; + size_t k; // nb of results per query in the GT + int64_t* gt; // nq * k matrix of ground-truth nearest-neighbors + knowhere::DatasetPtr xq_dataset; + + double train_time_min, train_time_max, train_time_avg; + double add_points_time_min, add_points_time_max, add_points_time_avg; + + Flat_Fixture_SIFT1M() { + std::cout << "[Benchmark] Flat_Fixture Benchmark Constructor only called once per fixture testcase hat uses it." << std::endl; + xq_dataset = load_queries(SIFT_DIM, nq); + load_ground_truth(nq, k, gt, conf); + } + + ~Flat_Fixture_SIFT1M() { + std::cout << "[Benchmark] FLAT_Fixture Benchmark Destructor." << std::endl; + ReleaseQuery(xq_dataset); + delete[] gt; + } + + void SetUp(::benchmark::State& state) { + std::cout << "[Benchmark] SetUp." << std::endl; + std::tie(train_time_min, train_time_max, train_time_avg) = train(d, nt, index, conf); + std::tie(add_points_time_min, add_points_time_max, add_points_time_avg) = add_points(index_type, d, nb, index, conf, nt); + index->UpdateIndexSize(); + state.counters["Index Size"] = index->Size(); + } + + void TearDown(const ::benchmark::State& state) { + std::cout << "[Benchmark] TearDown." << std::endl; + } +}; + +BENCHMARK_DEFINE_F(Flat_Fixture_SIFT1M, Flat_SIFT1M)(benchmark::State& state) { + auto result = milvus::knowhere::DatasetPtr(nullptr); + { + std::cout << "[Benchmark] Perform a search on " << nq << " queries" << std::endl; + std::cout << para; + // Wash the cache + // result = index->Query(xq_dataset, conf, nullptr); + // ReleaseQueryResult(result); + for (auto _ : state) { + result = index->Query(xq_dataset, conf, nullptr); + } + } + + // QPS + state.SetItemsProcessed(state.iterations() * nq); + + auto recall = compute_recall(nq, k, result, gt, k); + ReleaseQueryResult(result); +} + +BENCHMARK_REGISTER_F(Flat_Fixture_SIFT1M, Flat_SIFT1M)->Unit(benchmark::kMillisecond); + diff --git a/internal/core/bench/parameter-tuning/bench_hnsw.cpp b/internal/core/bench/parameter-tuning/bench_hnsw.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b124756d66800b415af66bd108be2c3858a2fe5 --- /dev/null +++ b/internal/core/bench/parameter-tuning/bench_hnsw.cpp @@ -0,0 +1,184 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under the License + +#include <benchmark/benchmark.h> + +#include "index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.h" +#include "index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.h" +#include "indexbuilder/IndexWrapper.h" +#include "indexbuilder/index_c.h" +#include "indexbuilder/utils.h" +#include "test_utils/indexbuilder_test_utils.h" +#include "bench_utils/parameter_tuning_utils.h" +#include "bench_utils/sift.h" + +namespace knowhere = milvus::knowhere; + +//auto metric_type_collections = [] { +// static std::unordered_map<int64_t, milvus::knowhere::MetricType> collections{ +// {0, milvus::knowhere::Metric::L2}, +// }; +// return collections; +//}(); + +//// auto is_binary = state.range(2); +//auto is_binary = false; +//auto dataset = GenDataset(NB, metric_type, is_binary); + +class HNSW_Fixture_SIFT1M : public benchmark::Fixture { +public: + const knowhere::IndexType index_type = knowhere::IndexEnum::INDEX_HNSW; + knowhere::MetricType metric_type = knowhere::Metric::L2; + knowhere::VecIndexPtr index = knowhere::VecIndexFactory::GetInstance().CreateVecIndex(index_type); + HNSW_Parameter para; + knowhere::Config conf = knowhere::Config{ + {knowhere::meta::DIM, 128}, + {knowhere::meta::TOPK, 100}, + {knowhere::IndexParams::M, -1}, + {knowhere::IndexParams::efConstruction, -1}, + {knowhere::IndexParams::ef, -1}, + {knowhere::Metric::TYPE, metric_type}, + }; + + size_t d; + size_t nt; + size_t nb; + size_t nq; + size_t k; // nb of results per query in the GT + int64_t* gt; // nq * k matrix of ground-truth nearest-neighbors + knowhere::DatasetPtr xq_dataset; + + double train_time_min, train_time_max, train_time_avg; + double add_points_time_min, add_points_time_max, add_points_time_avg; + + HNSW_Fixture_SIFT1M() { + std::cout << "[Benchmark] HNSW_Fixture Benchmark Constructor only called once per fixture testcase hat uses it." << std::endl; + xq_dataset = load_queries(SIFT_DIM, nq); + load_ground_truth(nq, k, gt, conf); + } + + ~HNSW_Fixture_SIFT1M() { + std::cout << "[Benchmark] HNSW_Fixture Benchmark Destructor." << std::endl; + ReleaseQuery(xq_dataset); + delete[] gt; + } + + void SetUp(::benchmark::State& state) { + std::cout << "[Benchmark] SetUp." << std::endl; + static bool needBuildIndex = true; + if (para.IsInvalid()) { + para.Set(state.range(0), state.range(1), state.range(2)); + conf[knowhere::IndexParams::M] = para.Get_m(); + assert(conf[knowhere::IndexParams::M] == para.Get_m()); + conf[knowhere::IndexParams::efConstruction] = para.Get_efConstruction(); + assert(conf[knowhere::IndexParams::efConstruction] == para.Get_efConstruction()); + conf[knowhere::IndexParams::ef] = para.Get_ef(); + assert(conf[knowhere::IndexParams::ef] == para.Get_ef()); + } + + // Build Parameters: m, Get_efConstruction + auto m = state.range(0); + state.counters["m"] = m; + auto efConstruction = state.range(1); + state.counters["efConstruction"] = efConstruction; + if (!para.CheckBuildPara(m, efConstruction)) { + std::cout << "[Benchmark] HNSW_Fixture Benchmark Constructor only called once per fixture testcase hat uses it." << std::endl; + conf[knowhere::IndexParams::M] = para.Get_m(); + assert(conf[knowhere::IndexParams::M] == para.Get_m()); + conf[knowhere::IndexParams::efConstruction] = para.Get_efConstruction(); + assert(conf[knowhere::IndexParams::efConstruction] == para.Get_efConstruction()); + needBuildIndex = true; + } + + // Search Parameters: ef + auto ef = state.range(2); + state.counters["ef"] = ef; + conf[knowhere::IndexParams::ef] = para.Get_ef(); + assert(conf[knowhere::IndexParams::ef] == para.Get_ef()); + para.SetSearchPara(ef); + + // Build Index if necessary + if (needBuildIndex) { + std::tie(train_time_min, train_time_max, train_time_avg) = train(d, nt, index, conf); + std::tie(add_points_time_min, add_points_time_max, add_points_time_avg) = add_points(index_type, d, nb, index, conf, nt); + needBuildIndex = false; + } + state.counters["train time min (ms)"] = train_time_min; + state.counters["train time max (ms)"] = train_time_max; + state.counters["train time avg (ms)"] = train_time_avg; + state.counters["add points time min (ms)"] = add_points_time_min; + state.counters["add points time max (ms)"] = add_points_time_max; + state.counters["add points time avg (ms)"] = add_points_time_avg; + index->UpdateIndexSize(); + state.counters["Index Size (B)"] = index->Size(); + } + + void TearDown(const ::benchmark::State& state) { + std::cout << "[Benchmark] TearDown." << std::endl; + } +}; + +BENCHMARK_DEFINE_F(HNSW_Fixture_SIFT1M, HNSW_SIFT1M)(benchmark::State& state) { + auto result = milvus::knowhere::DatasetPtr(nullptr); + conf[knowhere::IndexParams::ef] = para.Get_ef(); + assert(para.Get_ef() == state.range(2)); + { + std::cout << "[Benchmark] Perform a search on " << nq << " queries" << std::endl; + std::cout << para; + // Wash the cache +// result = index->Query(xq_dataset, conf, nullptr); +// ReleaseQueryResult(result); + for (auto _ : state) { + result = index->Query(xq_dataset, conf, nullptr); + } + } + + // QPS + state.SetItemsProcessed(state.iterations() * nq); + + auto recall = compute_recall(nq, k, result, gt, k); + state.counters["Recall"] = recall; + auto recall_1 = compute_recall(nq, k, result, gt, 1); + state.counters["Recall@1"] = recall_1; + if (k >= 10) { + auto recall_10 = compute_recall(nq, k, result, gt, 10); + state.counters["Recall@10"] = recall_10; + } + if (k >= 100) { + auto recall_100 = compute_recall(nq, k, result, gt, 100); + state.counters["Recall@100"] = recall_100; + } + + ReleaseQueryResult(result); +} + +// m \in [4, ..., 64] +// efConstruction \in [8, ..., 512] +// ef \in [k, ..., 32768] +static void +CustomArguments(benchmark::internal::Benchmark* b) { + for (int m = 4; m <= 64; ++m) { + for (int efConstruction = 8; efConstruction <= 512; efConstruction *= 2) { + for (int ef = 100 /*TODO: the TOPK*/; ef <= 32768; ef *= 2) { + b->Args({m, efConstruction, ef}); + } + } + } +// for (int m = 16; m <= 64; m *= 2) { +// for (int efConstruction = 200; efConstruction <= 512; efConstruction *= 2) { +//// for (int ef = 100 /*TODO: the TOPK*/; ef <= 32768; ef *= 2) { +// b->Args({m, efConstruction, 200}); +//// } +// } +// } +} + +BENCHMARK_REGISTER_F(HNSW_Fixture_SIFT1M, HNSW_SIFT1M)->Unit(benchmark::kMillisecond)->Apply(CustomArguments); diff --git a/internal/core/bench/parameter-tuning/bench_ivfflat.cpp b/internal/core/bench/parameter-tuning/bench_ivfflat.cpp index 612cb02914f84a61103160f0e1fa13436fd34111..ec3e1de82b198e49b8acd8dae6ed24e58872c1be 100644 --- a/internal/core/bench/parameter-tuning/bench_ivfflat.cpp +++ b/internal/core/bench/parameter-tuning/bench_ivfflat.cpp @@ -22,6 +22,17 @@ namespace knowhere = milvus::knowhere; +//auto metric_type_collections = [] { +// static std::unordered_map<int64_t, milvus::knowhere::MetricType> collections{ +// {0, milvus::knowhere::Metric::L2}, +// }; +// return collections; +//}(); + +//// auto is_binary = state.range(2); +//auto is_binary = false; +//auto dataset = GenDataset(NB, metric_type, is_binary); + class IVFFlat_Fixture_SIFT1M : public benchmark::Fixture { public: const knowhere::IndexType index_type = knowhere::IndexEnum::INDEX_FAISS_IVFFLAT; @@ -53,11 +64,19 @@ class IVFFlat_Fixture_SIFT1M : public benchmark::Fixture { IVFFlat_Fixture_SIFT1M() { std::cout << "[Benchmark] IVFFlat_Fixture Benchmark Constructor only called once per fixture testcase hat uses it." << std::endl; + xq_dataset = load_queries(SIFT_DIM, nq); + load_ground_truth(nq, k, gt, conf); + } + + ~IVFFlat_Fixture_SIFT1M() { + std::cout << "[Benchmark] IVFPQ_Fixture Benchmark Destructor." << std::endl; + ReleaseQuery(xq_dataset); + delete[] gt; } void SetUp(::benchmark::State& state) { + std::cout << "[Benchmark] SetUp." << std::endl; static bool needBuildIndex = true; - static bool needLoadQuery = true; if (para.IsInvalid()) { para.Set(state.range(0), state.range(1)); conf[knowhere::IndexParams::nlist] = para.Get_nlist(); @@ -93,15 +112,11 @@ class IVFFlat_Fixture_SIFT1M : public benchmark::Fixture { state.counters["add points time avg"] = add_points_time_avg; index->UpdateIndexSize(); state.counters["Index Size"] = index->Size(); - - if (needLoadQuery) { - xq_dataset = load_queries(d, nq); - load_ground_truth(nq, k, gt, conf); - } - needLoadQuery = false; } - void TearDown(const ::benchmark::State& state) {} + void TearDown(const ::benchmark::State& state) { + std::cout << "[Benchmark] TearDown." << std::endl; + } }; BENCHMARK_DEFINE_F(IVFFlat_Fixture_SIFT1M, IVFFlat_SIFT1M)(benchmark::State& state) { @@ -112,7 +127,8 @@ BENCHMARK_DEFINE_F(IVFFlat_Fixture_SIFT1M, IVFFlat_SIFT1M)(benchmark::State& sta std::cout << "[Benchmark] Perform a search on " << nq << " queries" << std::endl; std::cout << para; // Wash the cache - result = index->Query(xq_dataset, conf, nullptr); +// result = index->Query(xq_dataset, conf, nullptr); +// ReleaseQueryResult(result); for (auto _ : state) { result = index->Query(xq_dataset, conf, nullptr); } @@ -133,8 +149,6 @@ BENCHMARK_DEFINE_F(IVFFlat_Fixture_SIFT1M, IVFFlat_SIFT1M)(benchmark::State& sta auto recall_100 = compute_recall(nq, k, result, gt, 100); state.counters["Recall@100"] = recall_100; } - -// ReleaseQuery(xq_dataset); ReleaseQueryResult(result); } @@ -143,7 +157,7 @@ BENCHMARK_DEFINE_F(IVFFlat_Fixture_SIFT1M, IVFFlat_SIFT1M)(benchmark::State& sta static void CustomArguments(benchmark::internal::Benchmark* b) { for (int nlist = 1024; nlist <= 65536; nlist *= 2) { - for (int nprobe = 1; nprobe <= nlist; nprobe *= 2) { + for (int nprobe = 1; nprobe <= 16; nprobe *= 2) { b->Args({nlist, nprobe}); } } diff --git a/internal/core/bench/parameter-tuning/bench_ivfpq.cpp b/internal/core/bench/parameter-tuning/bench_ivfpq.cpp index ee390812e257b6e70a88bf707ab942e6028f00f1..72f5ab5466b30414989c890e5043882535105fb6 100644 --- a/internal/core/bench/parameter-tuning/bench_ivfpq.cpp +++ b/internal/core/bench/parameter-tuning/bench_ivfpq.cpp @@ -22,6 +22,17 @@ namespace knowhere = milvus::knowhere; +//auto metric_type_collections = [] { +// static std::unordered_map<int64_t, milvus::knowhere::MetricType> collections{ +// {0, milvus::knowhere::Metric::L2}, +// }; +// return collections; +//}(); + +//// auto is_binary = state.range(2); +//auto is_binary = false; +//auto dataset = GenDataset(NB, metric_type, is_binary); + class IVFPQ_Fixture_SIFT1M : public benchmark::Fixture { public: const knowhere::IndexType index_type = knowhere::IndexEnum::INDEX_FAISS_IVFPQ; @@ -53,13 +64,18 @@ public: double train_time_min, train_time_max, train_time_avg; double add_points_time_min, add_points_time_max, add_points_time_avg; + bool needBuildIndex = true; + IVFPQ_Fixture_SIFT1M() { std::cout << "[Benchmark] IVFPQ_Fixture Benchmark Constructor only called once per fixture testcase hat uses it." << std::endl; } + ~IVFPQ_Fixture_SIFT1M() { + std::cout << "[Benchmark] IVFPQ_Fixture Benchmark Destructor." << std::endl; + } + void SetUp(::benchmark::State& state) { - static bool needBuildIndex = true; - static bool needLoadQuery = true; + std::cout << "[Benchmark] SetUp." << std::endl; if (para.IsInvalid()) { para.Set(state.range(0), state.range(2), state.range(3), SIFT_DIM, state.range(1)); conf[knowhere::IndexParams::nlist] = para.Get_nlist(); @@ -105,14 +121,15 @@ public: index->UpdateIndexSize(); state.counters["Index Size (B)"] = index->Size(); - if (needLoadQuery) { - xq_dataset = load_queries(d, nq); - load_ground_truth(nq, k, gt, conf); - } - needLoadQuery = false; + xq_dataset = load_queries(SIFT_DIM, nq); + load_ground_truth(nq, k, gt, conf); } - void TearDown(const ::benchmark::State& state) {} + void TearDown(const ::benchmark::State& state) { + std::cout << "[Benchmark] TearDown." << std::endl; + ReleaseQuery(xq_dataset); + delete[] gt; + } }; BENCHMARK_DEFINE_F(IVFPQ_Fixture_SIFT1M, IVFPQ_SIFT1M)(benchmark::State& state) { @@ -123,7 +140,8 @@ BENCHMARK_DEFINE_F(IVFPQ_Fixture_SIFT1M, IVFPQ_SIFT1M)(benchmark::State& state) std::cout << "[Benchmark] Perform a search on " << nq << " queries" << std::endl; std::cout << para; // Wash the cache - result = index->Query(xq_dataset, conf, nullptr); +// result = index->Query(xq_dataset, conf, nullptr); +// ReleaseQueryResult(result); for (auto _ : state) { result = index->Query(xq_dataset, conf, nullptr); } @@ -145,7 +163,6 @@ BENCHMARK_DEFINE_F(IVFPQ_Fixture_SIFT1M, IVFPQ_SIFT1M)(benchmark::State& state) state.counters["Recall@100"] = recall_100; } -// ReleaseQuery(xq_dataset); ReleaseQueryResult(result); } @@ -155,15 +172,24 @@ BENCHMARK_DEFINE_F(IVFPQ_Fixture_SIFT1M, IVFPQ_SIFT1M)(benchmark::State& state) // m \in DIM % m == 0 static void CustomArguments(benchmark::internal::Benchmark* b) { - for (int nlist = 1024; nlist <= 65536; nlist *= 2) { - for (int nbits = 1; nbits <= 16; nbits++) { + for (int nlist = 1024; nlist <= 2018; nlist *= 2) { + for (int nbits = 1; nbits <= 2; nbits++) { for (int m = 1; SIFT_DIM % m == 0; m++) { - for (int nprobe = 1; nprobe <= nlist; nprobe *= 2) { - b->Args({nlist, nprobe, nbits, m}); - } +// for (int nprobe = 1; nprobe <= nlist; nprobe *= 2) { + b->Args({nlist, nlist, nbits, m}); +// } } } } +// for (int nlist = 1024; nlist <= 65536; nlist *= 2) { +// for (int nbits = 1; nbits <= 16; nbits++) { +// for (int m = 1; SIFT_DIM % m == 0; m++) { +// for (int nprobe = 1; nprobe <= nlist; nprobe *= 2) { +// b->Args({nlist, nprobe, nbits, m}); +// } +// } +// } +// } } // ->Name("IVF_PQ/L2/VectorFloat") diff --git a/internal/core/bench/parameter-tuning/bench_ivfsq8.cpp b/internal/core/bench/parameter-tuning/bench_ivfsq8.cpp index 55cf90f6228aee721492bd683890948288ca43a6..2cac7d1f1a64ee8a4fc346eb6f848c9ef132f344 100644 --- a/internal/core/bench/parameter-tuning/bench_ivfsq8.cpp +++ b/internal/core/bench/parameter-tuning/bench_ivfsq8.cpp @@ -22,6 +22,17 @@ namespace knowhere = milvus::knowhere; +//auto metric_type_collections = [] { +// static std::unordered_map<int64_t, milvus::knowhere::MetricType> collections{ +// {0, milvus::knowhere::Metric::L2}, +// }; +// return collections; +//}(); + +//// auto is_binary = state.range(2); +//auto is_binary = false; +//auto dataset = GenDataset(NB, metric_type, is_binary); + class IVFSQ8_Fixture_SIFT1M : public benchmark::Fixture { public: const knowhere::IndexType index_type = knowhere::IndexEnum::INDEX_FAISS_IVFSQ8; @@ -54,11 +65,19 @@ public: IVFSQ8_Fixture_SIFT1M() { std::cout << "[Benchmark] IVFSQ8_Fixture Benchmark Constructor only called once per fixture testcase hat uses it." << std::endl; + xq_dataset = load_queries(SIFT_DIM, nq); + load_ground_truth(nq, k, gt, conf); + } + + ~IVFSQ8_Fixture_SIFT1M() { + std::cout << "[Benchmark] IVFPQ_Fixture Benchmark Destructor." << std::endl; + ReleaseQuery(xq_dataset); + delete[] gt; } void SetUp(::benchmark::State& state) { + std::cout << "[Benchmark] SetUp." << std::endl; static bool needBuildIndex = true; - static bool needLoadQuery = true; if (para.IsInvalid()) { para.Set(state.range(0), state.range(1)); // para.Set(state.range(0), state.range(2), state.range(1)); @@ -100,15 +119,11 @@ public: state.counters["add points time avg (ms)"] = add_points_time_avg; index->UpdateIndexSize(); state.counters["Index Size (B)"] = index->Size(); - - if (needLoadQuery) { - xq_dataset = load_queries(d, nq); - load_ground_truth(nq, k, gt, conf); - } - needLoadQuery = false; } - void TearDown(const ::benchmark::State& state) {} + void TearDown(const ::benchmark::State& state) { + std::cout << "[Benchmark] TearDown." << std::endl; + } }; BENCHMARK_DEFINE_F(IVFSQ8_Fixture_SIFT1M, IVFSQ8_SIFT1M)(benchmark::State& state) { @@ -119,7 +134,8 @@ BENCHMARK_DEFINE_F(IVFSQ8_Fixture_SIFT1M, IVFSQ8_SIFT1M)(benchmark::State& state std::cout << "[Benchmark] Perform a search on " << nq << " queries" << std::endl; std::cout << para; // Wash the cache - result = index->Query(xq_dataset, conf, nullptr); +// result = index->Query(xq_dataset, conf, nullptr); +// ReleaseQueryResult(result); for (auto _ : state) { result = index->Query(xq_dataset, conf, nullptr); } @@ -141,7 +157,6 @@ BENCHMARK_DEFINE_F(IVFSQ8_Fixture_SIFT1M, IVFSQ8_SIFT1M)(benchmark::State& state state.counters["Recall@100"] = recall_100; } -// ReleaseQuery(xq_dataset); ReleaseQueryResult(result); } diff --git a/internal/core/src/index/unittest/test_hnsw.cpp b/internal/core/src/index/unittest/test_hnsw.cpp index 4fb14f8dbbb102099fb06842802a54185dc06fee..54fd931e6249de0f3a544242ff56043fa3323a1d 100644 --- a/internal/core/src/index/unittest/test_hnsw.cpp +++ b/internal/core/src/index/unittest/test_hnsw.cpp @@ -45,6 +45,20 @@ class HNSWTest : public DataGen, public TestWithParam<std::string> { INSTANTIATE_TEST_CASE_P(HNSWParameters, HNSWTest, Values("HNSW")); +TEST_P(HNSWTest, HNSW_Pass) { +// Generate(64, 10000, 10); // dim = 64, nb = 10000, nq = 10 +// index_->Train(base_dataset, conf); +// Generate(64, 10000, 10); // dim = 64, nb = 10000, nq = 10 + index_->AddWithoutIds(base_dataset, conf); +} + +TEST_P(HNSWTest, HNSW_NotPass) { + Generate(64, 10000, 10); // dim = 64, nb = 10000, nq = 10 + index_->Train(base_dataset, conf); + Generate(64, 10000 + 1, 10); // dim = 64, nb = 10000, nq = 10 + index_->AddWithoutIds(base_dataset, conf); +} + TEST_P(HNSWTest, HNSW_basic) { assert(!xb.empty());