Skip to content

Commit bf6eb1a

Browse files
[8.2] [MOD-11237] Block Size Boundary Oscillation Benchmark (#769)
[MOD-11237] Block Size Boundary Oscillation Benchmark (#768) * imp bemchmar fix counters * fix (cherry picked from commit fd08b55) Co-authored-by: meiravgri <109056284+meiravgri@users.noreply.github.com>
1 parent da267a5 commit bf6eb1a

File tree

4 files changed

+109
-5
lines changed

4 files changed

+109
-5
lines changed

tests/benchmark/bm_common.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ void BM_VecSimCommon<index_type_t>::Memory(benchmark::State &st, IndexTypeIndex
7070
for (auto _ : st) {
7171
// Do nothing...
7272
}
73-
st.counters["memory"] = (double)VecSimIndex_StatsInfo(index).memory;
73+
st.counters["memory"] =
74+
benchmark::Counter((double)VecSimIndex_StatsInfo(index).memory,
75+
benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024);
7476
}
7577

7678
// TopK search BM

tests/benchmark/bm_vecsim_basics.h

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ class BM_VecSimBasics : public BM_VecSimCommon<index_type_t> {
4242
static void Range_BF(benchmark::State &st);
4343
static void Range_HNSW(benchmark::State &st);
4444

45+
// Reproduces allocation/deallocation oscillation issue at block size boundaries.
46+
// Sets up index at blockSize+1 capacity, then repeatedly deletes and re-adds the same vector,
47+
// triggering constant grow-shrink cycles.
48+
// This behavior was fixed by PR #753 with a conservative resize strategy that only
49+
// shrinks containers when there are 2+ free blocks, preventing oscillation cycles.
50+
// Expected: High allocation overhead before fix, stable performance after fix.
51+
static void UpdateAtBlockSize(benchmark::State &st);
52+
4553
private:
4654
// Vectors of vector to store deleted labels' data.
4755
using LabelData = std::vector<std::vector<data_t>>;
@@ -76,7 +84,9 @@ void BM_VecSimBasics<index_type_t>::AddLabel(benchmark::State &st) {
7684
// For tiered index, wait for all threads to finish indexing
7785
BM_VecSimGeneral::mock_thread_pool->thread_pool_wait();
7886

79-
st.counters["memory_per_vector"] = (double)memory_delta / (double)added_vec_count;
87+
st.counters["memory_per_vector"] =
88+
benchmark::Counter((double)memory_delta / (double)added_vec_count,
89+
benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024);
8090
st.counters["vectors_per_label"] = vec_per_label;
8191

8292
assert(VecSimIndex_IndexSize(index) == N_VECTORS + added_vec_count);
@@ -123,7 +133,9 @@ void BM_VecSimBasics<index_type_t>::AddLabel_AsyncIngest(benchmark::State &st) {
123133
}
124134

125135
size_t memory_delta = index->getAllocationSize() - memory_before;
126-
st.counters["memory_per_vector"] = (double)memory_delta / (double)added_vec_count;
136+
st.counters["memory_per_vector"] =
137+
benchmark::Counter((double)memory_delta / (double)added_vec_count,
138+
benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024);
127139
st.counters["vectors_per_label"] = vec_per_label;
128140
st.counters["num_threads"] = BM_VecSimGeneral::mock_thread_pool->thread_pool_size;
129141

@@ -173,7 +185,9 @@ void BM_VecSimBasics<index_type_t>::DeleteLabel(algo_t *index, benchmark::State
173185
if (VecSimIndex_BasicInfo(index).algo == VecSimAlgo_TIERED) {
174186
dynamic_cast<TieredHNSWIndex<data_t, dist_t> *>(index)->executeReadySwapJobs();
175187
}
176-
st.counters["memory_per_vector"] = memory_delta / (double)removed_vectors_count;
188+
st.counters["memory_per_vector"] =
189+
benchmark::Counter((double)memory_delta / (double)removed_vectors_count,
190+
benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024);
177191

178192
// Restore index state.
179193
// For each label in removed_labels_data
@@ -223,7 +237,10 @@ void BM_VecSimBasics<index_type_t>::DeleteLabel_AsyncRepair(benchmark::State &st
223237
// Avg. memory delta per vector equals the total memory delta divided by the number
224238
// of deleted vectors.
225239
double memory_delta = tiered_index->getAllocationSize() - memory_before;
226-
st.counters["memory_per_vector"] = memory_delta / (double)removed_vectors_count;
240+
241+
st.counters["memory_per_vector"] =
242+
benchmark::Counter((double)memory_delta / (double)removed_vectors_count,
243+
benchmark::Counter::kDefaults, benchmark::Counter::OneK::kIs1024);
227244
st.counters["num_threads"] = (double)BM_VecSimGeneral::mock_thread_pool->thread_pool_size;
228245
st.counters["num_zombies"] = tiered_index->idToSwapJob.size();
229246

@@ -294,6 +311,69 @@ void BM_VecSimBasics<index_type_t>::Range_HNSW(benchmark::State &st) {
294311
st.counters["Recall"] = (float)total_res / total_res_bf;
295312
}
296313

314+
template <typename index_type_t>
315+
void BM_VecSimBasics<index_type_t>::UpdateAtBlockSize(benchmark::State &st) {
316+
auto index = GET_INDEX(st.range(0));
317+
size_t initial_index_size = VecSimIndex_IndexSize(index);
318+
// Calculate vectors needed to reach next block boundary
319+
size_t vecs_to_blocksize =
320+
BM_VecSimGeneral::block_size - (initial_index_size % BM_VecSimGeneral::block_size);
321+
assert(vecs_to_blocksize < BM_VecSimGeneral::block_size);
322+
labelType initial_label_count = index->indexLabelCount();
323+
labelType curr_label = initial_label_count;
324+
325+
// Set up index at blockSize+1 to trigger oscillation issue
326+
// Make sure we have enough queries to add a new label.
327+
assert(N_QUERIES > BM_VecSimGeneral::block_size);
328+
size_t overhead = 1;
329+
size_t added_vec_count = vecs_to_blocksize + overhead;
330+
for (size_t i = 0; i < added_vec_count; ++i) {
331+
VecSimIndex_AddVector(index, QUERIES[added_vec_count % N_QUERIES].data(), curr_label++);
332+
}
333+
// For tiered index, wait for all threads to finish indexing
334+
BM_VecSimGeneral::mock_thread_pool->thread_pool_wait();
335+
assert(VecSimIndex_IndexSize(index) % BM_VecSimGeneral::block_size == overhead);
336+
assert(VecSimIndex_IndexSize(index) == N_VECTORS + added_vec_count);
337+
338+
std::cout << "Added " << added_vec_count << " vectors to reach block size boundary."
339+
<< std::endl;
340+
std::cout << "Index size is now " << VecSimIndex_IndexSize(index) << std::endl;
341+
std::cout << "Last label is " << curr_label - 1 << std::endl;
342+
343+
// Benchmark loop: repeatedly delete/add same vector to trigger grow-shrink cycles
344+
labelType label_to_update = curr_label - 1;
345+
size_t index_cap = index->indexCapacity();
346+
for (auto _ : st) {
347+
// Remove the vector directly from hnsw
348+
size_t ret = VecSimIndex_DeleteVector(
349+
GET_INDEX(st.range(0) == INDEX_TIERED_HNSW ? INDEX_HNSW : st.range(0)),
350+
label_to_update);
351+
assert(ret == 1);
352+
assert(index->indexCapacity() == index_cap - BM_VecSimGeneral::block_size);
353+
// Capacity should shrink by one block after deletion
354+
ret = VecSimIndex_AddVector(index, QUERIES[(added_vec_count - 1) % N_QUERIES].data(),
355+
label_to_update);
356+
assert(ret == 1);
357+
BM_VecSimGeneral::mock_thread_pool->thread_pool_wait();
358+
assert(VecSimIndex_IndexSize(
359+
GET_INDEX(st.range(0) == INDEX_TIERED_HNSW ? INDEX_HNSW : st.range(0))) ==
360+
N_VECTORS + added_vec_count);
361+
// Capacity should grow back to original size after addition
362+
assert(index->indexCapacity() == index_cap);
363+
}
364+
assert(VecSimIndex_IndexSize(index) == N_VECTORS + added_vec_count);
365+
366+
// Clean-up all the new vectors to restore the index size to its original value.
367+
368+
size_t new_label_count = index->indexLabelCount();
369+
for (size_t label = initial_label_count; label < new_label_count; label++) {
370+
// If index is tiered HNSW, remove directly from the underline HNSW.
371+
VecSimIndex_DeleteVector(
372+
GET_INDEX(st.range(0) == INDEX_TIERED_HNSW ? INDEX_HNSW : st.range(0)), label);
373+
}
374+
assert(VecSimIndex_IndexSize(index) == N_VECTORS);
375+
}
376+
297377
#define UNIT_AND_ITERATIONS Unit(benchmark::kMillisecond)->Iterations(BM_VecSimGeneral::block_size)
298378

299379
// These macros are used to make sure the expansion of other macros happens when needed
@@ -345,3 +425,8 @@ void BM_VecSimBasics<index_type_t>::Range_HNSW(benchmark::State &st) {
345425
}
346426
#define REGISTER_DeleteLabel(BM_FUNC) \
347427
BENCHMARK_REGISTER_F(BM_VecSimBasics, BM_FUNC)->UNIT_AND_ITERATIONS
428+
429+
#define REGISTER_UpdateAtBlockSize(BM_FUNC, VecSimAlgo) \
430+
BENCHMARK_REGISTER_F(BM_VecSimBasics, BM_FUNC) \
431+
->UNIT_AND_ITERATIONS->Arg(VecSimAlgo) \
432+
->ArgName(#VecSimAlgo)

tests/benchmark/run_files/bm_basics_multi_fp32.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,12 @@ DEFINE_DELETE_LABEL(BM_FUNC_NAME(DeleteLabel, Tiered), fp32_index_t, TieredHNSWI
3535
INDEX_TIERED_HNSW)
3636
#include "benchmark/bm_initialization/bm_basics_initialize_fp32.h"
3737

38+
// Test oscillations at block size boundaries.
39+
BENCHMARK_TEMPLATE_DEFINE_F(BM_VecSimBasics, CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Multi),
40+
fp32_index_t)
41+
(benchmark::State &st) { UpdateAtBlockSize(st); }
42+
REGISTER_UpdateAtBlockSize(CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Multi), INDEX_BF);
43+
REGISTER_UpdateAtBlockSize(CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Multi), INDEX_HNSW);
44+
REGISTER_UpdateAtBlockSize(CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Multi),
45+
INDEX_TIERED_HNSW);
3846
BENCHMARK_MAIN();

tests/benchmark/run_files/bm_basics_single_fp32.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,14 @@ DEFINE_DELETE_LABEL(BM_FUNC_NAME(DeleteLabel, HNSW), fp32_index_t, HNSWIndex_Sin
3131
INDEX_HNSW)
3232
DEFINE_DELETE_LABEL(BM_FUNC_NAME(DeleteLabel, Tiered), fp32_index_t, TieredHNSWIndex, float, float,
3333
INDEX_TIERED_HNSW)
34+
35+
// Test Oscilations
36+
BENCHMARK_TEMPLATE_DEFINE_F(BM_VecSimBasics, CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Single),
37+
fp32_index_t)
38+
(benchmark::State &st) { UpdateAtBlockSize(st); }
39+
REGISTER_UpdateAtBlockSize(CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Single), INDEX_BF);
40+
REGISTER_UpdateAtBlockSize(CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Single), INDEX_HNSW);
41+
REGISTER_UpdateAtBlockSize(CONCAT_WITH_UNDERSCORE_ARCH(UpdateAtBlockSize, Single),
42+
INDEX_TIERED_HNSW);
3443
#include "benchmark/bm_initialization/bm_basics_initialize_fp32.h"
3544
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)