Skip to content

Commit cb0679e

Browse files
committed
test(regression): add portable DeterministicUniformIntDistribution for CI
1 parent c64b6b9 commit cb0679e

5 files changed

Lines changed: 64 additions & 15 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <random>
5+
#include <type_traits>
6+
7+
// Portable uniform integer distribution.
8+
// std::uniform_int_distribution is NOT portable across compilers/platforms:
9+
// - MSVC STL, libstdc++, and libc++ use different algorithm implementations/rejection sampling logic.
10+
// - The same seed produces different sequences on different platforms.
11+
// - This makes regression tests non-deterministic across CI runners.
12+
//
13+
// This implementation uses bounded rejection sampling over std::mt19937 output to guarantee
14+
// identical integer sequences across Windows, Linux, and macOS (x86_64 and ARM64).
15+
template <typename IntType = int>
16+
class DeterministicUniformIntDistribution {
17+
public:
18+
DeterministicUniformIntDistribution(IntType min_val, IntType max_val)
19+
: min_val_(min_val), max_val_(max_val) {
20+
if (min_val_ < max_val_) {
21+
range_ = static_cast<uint64_t>(max_val_) - static_cast<uint64_t>(min_val_) + 1;
22+
max_valid_ = (4294967296ULL / range_) * range_ - 1;
23+
} else {
24+
range_ = 1;
25+
max_valid_ = 4294967295ULL;
26+
}
27+
}
28+
29+
template <typename Generator>
30+
IntType operator()(Generator& g) {
31+
if (range_ <= 1) {
32+
return min_val_;
33+
}
34+
35+
uint32_t val;
36+
do {
37+
val = g();
38+
} while (static_cast<uint64_t>(val) > max_valid_);
39+
40+
return static_cast<IntType>(min_val_ + (val % range_));
41+
}
42+
43+
private:
44+
IntType min_val_;
45+
IntType max_val_;
46+
uint64_t range_{1};
47+
uint64_t max_valid_{4294967295ULL};
48+
};

cpp/test/src/regression/metric/test_innerproduct_regression.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,22 @@ TEST(DeglibRegressionIP, MultiInstructionSetBenchmark)
1919
// reducing QPS noise from OS jitter and CPU power-state transitions.
2020
#if defined(DEGLIB_X86)
2121
if (deglib::cpu::has_avx512()) {
22-
run_regression_test("AVX512_16Ext", deglib::Metric::InnerProduct, 18000.0, 10.3, 0.774,
22+
run_regression_test("AVX512_16Ext", deglib::Metric::InnerProduct, 18000.0, 10.0, 0.774,
2323
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
2424
deglib::distances::fp32_ip::InnerProductFloat16Ext_AVX512{}, 50);
2525
}
2626
if (deglib::cpu::has_avx2()) {
27-
run_regression_test("AVX2_16Ext", deglib::Metric::InnerProduct, 18000.0, 10.2, 0.774,
27+
run_regression_test("AVX2_16Ext", deglib::Metric::InnerProduct, 18000.0, 10.0, 0.774,
2828
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
2929
deglib::distances::fp32_ip::InnerProductFloat16Ext_AVX2{}, 50);
3030
}
3131
if (deglib::cpu::has_sse42()) {
32-
run_regression_test("SSE_16Ext", deglib::Metric::InnerProduct, 15000.0, 11.8, 0.774,
32+
run_regression_test("SSE_16Ext", deglib::Metric::InnerProduct, 15000.0, 11.5, 0.774,
3333
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
3434
deglib::distances::fp32_ip::InnerProductFloat16Ext_SSE{}, 50);
3535
}
3636
#endif
37-
run_regression_test("Scalar", deglib::Metric::InnerProduct, 12000.0, 15.4, 0.78,
37+
run_regression_test("Scalar", deglib::Metric::InnerProduct, 11500.0, 15.4, 0.78,
3838
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
3939
deglib::distances::fp32_ip::InnerProductFloat{}, 50);
4040
}

cpp/test/src/regression/metric/test_l2_regression.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ TEST(DeglibRegressionL2, MultiInstructionSetBenchmark)
1919
// reducing QPS noise from OS jitter and CPU power-state transitions.
2020
#if defined(DEGLIB_X86)
2121
if (deglib::cpu::has_avx512()) {
22-
run_regression_test("AVX512_16Ext", deglib::Metric::L2, 38000.0, 6.1, 0.92,
22+
run_regression_test("AVX512_16Ext", deglib::Metric::L2, 42000.0, 6.0, 0.92,
2323
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
2424
deglib::distances::fp32_l2::L2Float16Ext_AVX512{}, 100);
2525
}
@@ -34,7 +34,7 @@ TEST(DeglibRegressionL2, MultiInstructionSetBenchmark)
3434
deglib::distances::fp32_l2::L2Float16Ext_SSE{}, 100);
3535
}
3636
#endif
37-
run_regression_test("Scalar", deglib::Metric::L2, 28000.0, 9.3, 0.92,
37+
run_regression_test("Scalar", deglib::Metric::L2, 29000.0, 9.3, 0.92,
3838
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
3939
deglib::distances::fp32_l2::L2Float{}, 100);
4040
}

cpp/test/src/regression/metric/test_l2_uint8_regression.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@ TEST(DeglibRegressionL2Uint8, MultiInstructionSetBenchmark)
1818
// reducing QPS noise from OS jitter and CPU power-state transitions.
1919
#if defined(DEGLIB_X86)
2020
if (deglib::cpu::has_avx512()) {
21-
run_regression_test("AVX512_Ext32", deglib::Metric::L2_Uint8, 73000.0, 4.7, 1,
21+
run_regression_test("AVX512_Ext32", deglib::Metric::L2_Uint8, 75000.0, 4.7, 0.96,
2222
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
23-
deglib::distances::uint8_l2::L2Uint8Ext32_AVX512{}, 100);
23+
deglib::distances::uint8_l2::L2Uint8Ext32_AVX512{}, 500);
2424
}
2525
if (deglib::cpu::has_avx2()) {
26-
run_regression_test("AVX2_Ext32", deglib::Metric::L2_Uint8, 74000.0, 4.8, 1,
26+
run_regression_test("AVX2_Ext32", deglib::Metric::L2_Uint8, 75000.0, 4.8, 0.96,
2727
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
28-
deglib::distances::uint8_l2::L2Uint8Ext32_AVX2{}, 100);
28+
deglib::distances::uint8_l2::L2Uint8Ext32_AVX2{}, 500);
2929
}
3030
if (deglib::cpu::has_sse42()) {
31-
run_regression_test("SSE_Ext32", deglib::Metric::L2_Uint8, 70000.0, 5.2, 1,
31+
run_regression_test("SSE_Ext32", deglib::Metric::L2_Uint8, 72000.0, 5.2, 0.96,
3232
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
33-
deglib::distances::uint8_l2::L2Uint8Ext32_SSE{}, 100);
33+
deglib::distances::uint8_l2::L2Uint8Ext32_SSE{}, 500);
3434
}
3535
#endif
36-
run_regression_test("Scalar", deglib::Metric::L2_Uint8, 63000.0, 5.5, 1,
36+
run_regression_test("Scalar", deglib::Metric::L2_Uint8, 65000.0, 5.5, 0.96,
3737
base_data.data(), query_data.data(), base_count, query_count, dim, gt_data,
38-
deglib::distances::uint8_l2::L2Uint8{}, 100);
38+
deglib::distances::uint8_l2::L2Uint8{}, 500);
3939
}
4040

4141
TEST(DeglibRegressionL2Uint8, DistanceRecallAllVariantsSameDataset)

cpp/test/src/regression/test_regression.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <gtest/gtest.h>
55

66
#include "deterministic_normal_distribution.h"
7+
#include "deterministic_uniform_int_distribution.h"
78

89
#include <algorithm>
910
#include <chrono>
@@ -140,7 +141,7 @@ inline static void generate_synthetic_clustered_dataset_uint8(size_t count, size
140141
query.resize(query_count * dim);
141142

142143
std::mt19937 rng(42);
143-
std::uniform_int_distribution<int> cent_dist(20, 235);
144+
DeterministicUniformIntDistribution<int> cent_dist(20, 235);
144145
DeterministicNormalDistribution noise_dist(-15.0f, 15.0f);
145146

146147
std::vector<std::vector<int>> centroids(num_clusters, std::vector<int>(dim));

0 commit comments

Comments
 (0)