Skip to content

Commit c801d8a

Browse files
[8.2] [SVS] Add unit test to validate the compute distance fix (#870)
[SVS] Add unit test to validate the compute distance fix (#858) * Add test with direct call to SVS distance computations * Use system page protection to catch the unmasked vector loading issue * Do not use pre-compiled SVS binaries but compile from sources * Update SVS submodule to the latest main with AVX2 fixes * Address code review comment (cherry picked from commit e493894) Co-authored-by: Rafik Saliev <[email protected]>
1 parent 5afb165 commit c801d8a

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

tests/unit/test_svs.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3156,6 +3156,75 @@ TEST(SVSTest, scalar_quantization_query) {
31563156
}
31573157
}
31583158

3159+
#if defined(__linux__) && defined(__x86_64__)
3160+
TEST(SVSTest, compute_distance) {
3161+
// Test svs::distance computation for custom data allocations and alignments
3162+
constexpr size_t dim = 4;
3163+
3164+
// get system pagesize
3165+
size_t page_size = sysconf(_SC_PAGESIZE);
3166+
ASSERT_GT(page_size, 16);
3167+
3168+
// Allocate two consecutive pages: one for data, one as a guard (inaccessible)
3169+
uint8_t *raw_a = (uint8_t *)mmap(nullptr, 2 * page_size, PROT_READ | PROT_WRITE,
3170+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
3171+
ASSERT_NE(raw_a, MAP_FAILED);
3172+
// Protect the second page to prevent access
3173+
ASSERT_EQ(mprotect(raw_a + page_size, page_size, PROT_NONE), 0);
3174+
3175+
// Allocate the second buffer
3176+
uint8_t *raw_b = (uint8_t *)mmap(nullptr, 2 * page_size, PROT_READ | PROT_WRITE,
3177+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
3178+
ASSERT_NE(raw_b, MAP_FAILED);
3179+
// Protect the second page to prevent access
3180+
ASSERT_EQ(mprotect(raw_b + page_size, page_size, PROT_NONE), 0);
3181+
3182+
// use last bytes of page for data
3183+
// Note: Accessing above 'dim' should trigger Memory Access Error.
3184+
constexpr size_t data_size = dim * sizeof(float);
3185+
float *a = reinterpret_cast<float *>(raw_a + page_size - data_size);
3186+
float *b = reinterpret_cast<float *>(raw_b + page_size - data_size);
3187+
3188+
std::iota(a, a + dim, 1.f);
3189+
std::iota(b, b + dim, 2.f);
3190+
3191+
// Verify default implementation
3192+
auto dist_l2 = svs::distance::compute(svs::DistanceL2{}, std::span(a, dim), std::span(b, dim));
3193+
EXPECT_GT(dist_l2, 0.0);
3194+
auto dist_ip = svs::distance::compute(svs::DistanceIP{}, std::span(a, dim), std::span(b, dim));
3195+
EXPECT_GT(dist_ip, 0.0);
3196+
3197+
// Verify AVX2 and AVX512 implementations
3198+
if (svs::detail::avx_runtime_flags.is_avx2_supported()) {
3199+
// AVX2 implementations
3200+
auto dist_l2_avx2 = svs::distance::
3201+
L2Impl<svs::Dynamic, float, float, svs::distance::AVX_AVAILABILITY::AVX2>::compute(
3202+
a, b, svs::lib::MaybeStatic(dim));
3203+
auto dist_ip_avx2 = svs::distance::
3204+
IPImpl<svs::Dynamic, float, float, svs::distance::AVX_AVAILABILITY::AVX2>::compute(
3205+
a, b, svs::lib::MaybeStatic(dim));
3206+
EXPECT_DOUBLE_EQ(dist_l2, dist_l2_avx2);
3207+
EXPECT_DOUBLE_EQ(dist_ip, dist_ip_avx2);
3208+
}
3209+
3210+
if (svs::detail::avx_runtime_flags.is_avx512f_supported()) {
3211+
// AVX512 implementations
3212+
auto dist_l2_avx512 = svs::distance::
3213+
L2Impl<svs::Dynamic, float, float, svs::distance::AVX_AVAILABILITY::AVX512>::compute(
3214+
a, b, svs::lib::MaybeStatic(dim));
3215+
auto dist_ip_avx512 = svs::distance::
3216+
IPImpl<svs::Dynamic, float, float, svs::distance::AVX_AVAILABILITY::AVX512>::compute(
3217+
a, b, svs::lib::MaybeStatic(dim));
3218+
EXPECT_DOUBLE_EQ(dist_l2, dist_l2_avx512);
3219+
EXPECT_DOUBLE_EQ(dist_ip, dist_ip_avx512);
3220+
}
3221+
3222+
// unmap pages
3223+
munmap(raw_a, 2 * page_size);
3224+
munmap(raw_b, 2 * page_size);
3225+
}
3226+
#endif // defined(__linux__) && defined(__x86_64__)
3227+
31593228
#else // HAVE_SVS
31603229

31613230
TEST(SVSTest, svs_not_supported) {

0 commit comments

Comments
 (0)