Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 27 additions & 28 deletions include/rabitqlib/clustering/detail/kmeans.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ namespace detail {
return change <= early_stop_threshold;
}

// Float vectors accumulated in double, in index order.
[[nodiscard]] inline double squared_norm_f64(const float* vector, size_t d) {
double result = 0.0;
for (size_t dim = 0; dim < d; ++dim) {
const double value = vector[dim];
result += value * value;
}
return result;
}

[[nodiscard]] inline double squared_l2_f64(const float* a, const float* b, size_t d) {
double result = 0.0;
for (size_t dim = 0; dim < d; ++dim) {
const double difference = static_cast<double>(a[dim]) - b[dim];
result += difference * difference;
}
return result;
}

// Inputs have already passed clustering validation. GEMM only screens candidates;
// final distances and tie breaking retain the scalar double-precision arithmetic.
// Optional previous assignments contain n valid centroid IDs and may alias the
Expand Down Expand Up @@ -96,10 +115,7 @@ inline void exact_assign(
}
distance += 1.0;
} else {
for (size_t dim = 0; dim < d; ++dim) {
const double difference = static_cast<double>(vector[dim]) - centroid[dim];
distance += difference * difference;
}
distance = squared_l2_f64(vector, centroid, d);
}
return distance;
};
Expand All @@ -126,12 +142,8 @@ inline void exact_assign(
#pragma omp for schedule(static)
for (std::ptrdiff_t cluster = 0; cluster < static_cast<std::ptrdiff_t>(k);
++cluster) {
double norm = 0.0;
for (size_t dim = 0; dim < d; ++dim) {
const double value = centroids[static_cast<size_t>(cluster) * d + dim];
norm += value * value;
}
centroid_norms[static_cast<size_t>(cluster)] = norm;
centroid_norms[static_cast<size_t>(cluster)] =
squared_norm_f64(centroids + static_cast<size_t>(cluster) * d, d);
}
#pragma omp for schedule(static)
for (std::ptrdiff_t block = 0; block < static_cast<std::ptrdiff_t>(blocks);
Expand All @@ -144,12 +156,7 @@ inline void exact_assign(
const size_t rows = std::min(point_block, n - first_point);
for (size_t row = 0; row < rows; ++row) {
const float* vector = x + (first_point + row) * d;
double norm = 0.0;
for (size_t dim = 0; dim < d; ++dim) {
const double value = vector[dim];
norm += value * value;
}
point_norms[row] = norm;
point_norms[row] = squared_norm_f64(vector, d);
best_distances[row] = std::numeric_limits<double>::infinity();
if (previous_assignments != nullptr) {
const size_t point = first_point + row;
Expand Down Expand Up @@ -484,14 +491,9 @@ class LloydKMeans : public Parameters {
// Means, normalization, and displacement share one worker team. Preserve
// the per-centroid arithmetic; empty rows are measured after refilling.
const auto cluster_shift = [&](size_t cluster) {
double result = 0;
for (size_t dim = 0; dim < d; ++dim) {
const size_t i = cluster * d + dim;
const double difference =
static_cast<double>(centroids[i]) - previous_centroids[i];
result += difference * difference;
}
return result;
return squared_l2_f64(
centroids.data() + cluster * d, previous_centroids.data() + cluster * d, d
);
};
double shift = 0;
#pragma omp parallel for num_threads(num_workers) reduction(+ : shift)
Expand Down Expand Up @@ -604,10 +606,7 @@ class LloydKMeans : public Parameters {
}

void normalize_centroid(float* centroid) const {
double squared_norm = 0;
for (size_t dim = 0; dim < d; ++dim) {
squared_norm += static_cast<double>(centroid[dim]) * centroid[dim];
}
const double squared_norm = squared_norm_f64(centroid, d);
if (squared_norm == 0) {
return;
}
Expand Down
35 changes: 25 additions & 10 deletions include/rabitqlib/index/hnsw/hnsw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#pragma once

#include <algorithm>
#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -228,6 +229,8 @@ class HierarchicalNSW {
// `remove` sets the high bit of a point's stored cluster ID. construct and load
// keep num_clusters_ at or below 2^31, so a real cluster ID never has it.
static constexpr PID kRemovedMask = 0x80000000U;
// Used when a search passes ef_search == 0.
static constexpr size_t kDefaultEfSearch = 10;
size_t max_elements_{0};
mutable std::atomic<size_t> num_stored_points_{0};
size_t num_removed_{0}; // Recovered from stored cluster-ID markers when loading.
Expand All @@ -237,7 +240,6 @@ class HierarchicalNSW {
size_t upper_layer_max_degree_{0};
size_t base_layer_max_degree_{0};
size_t ef_construction_{0};
size_t default_ef_search_{0};
MetricType metric_type_{METRIC_L2};

double mult_{0.0};
Expand Down Expand Up @@ -314,12 +316,6 @@ class HierarchicalNSW {
);
}

PID* get_external_label_pt(PID internal_id) {
return reinterpret_cast<PID*>(
data_level0_memory_.get() + (internal_id * level0_record_bytes_) + label_offset_
);
}

char* get_bindata_by_internalid(PID internal_id) {
return data_level0_memory_.get() + (internal_id * level0_record_bytes_) +
bin_data_offset_;
Expand Down Expand Up @@ -356,13 +352,32 @@ class HierarchicalNSW {
return (get_raw_clusterid(internal_id) & kRemovedMask) != 0;
}

char* get_clusterid_pt(PID internal_id) {
return reinterpret_cast<char*>(
// Store a cluster ID, including any removal mark.
void set_raw_clusterid(PID internal_id, PID cluster_id) {
memcpy(
data_level0_memory_.get() + (internal_id * level0_record_bytes_) +
level0_links_bytes_
level0_links_bytes_,
&cluster_id,
sizeof(PID)
);
}

// Derive record sizes and offsets from padded_dim_, ex_bits_ and the layer degrees.
void set_record_layout();

// The fields set_record_layout derives, in a fixed order for comparison.
[[nodiscard]] std::array<size_t, 8> record_layout() const {
return {
bin_data_bytes_,
ex_data_bytes_,
level0_links_bytes_,
label_offset_,
bin_data_offset_,
ex_data_offset_,
level0_record_bytes_,
upper_layer_links_bytes_};
}

int get_random_level(double reverse_size);

// Write the index file at exactly this path.
Expand Down
137 changes: 28 additions & 109 deletions include/rabitqlib/utils/memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,185 +175,104 @@ static inline void prefetch_l2(const void* addr) {
#endif
}

inline void mem_prefetch_l1(const char* ptr, size_t num_lines) {
// Prefetch num_lines 64-byte lines (at most 20) starting at ptr.
template <void (*Prefetch)(const void*)>
inline void mem_prefetch_lines(const char* ptr, size_t num_lines) {
// The repeated fallthrough branches intentionally unroll up to 20 prefetches.
// NOLINTBEGIN(bugprone-branch-clone)
switch (num_lines) {
default:
[[fallthrough]];
case 20:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 19:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 18:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 17:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 16:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 15:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 14:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 13:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 12:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 11:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 10:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 9:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 8:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 7:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 6:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 5:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 4:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 3:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 2:
prefetch_l1(ptr);
Prefetch(ptr);
ptr += 64;
[[fallthrough]];
case 1:
prefetch_l1(ptr);
Prefetch(ptr);
[[fallthrough]];
case 0:
break;
}
// NOLINTEND(bugprone-branch-clone)
}

inline void mem_prefetch_l1(const char* ptr, size_t num_lines) {
mem_prefetch_lines<prefetch_l1>(ptr, num_lines);
}

inline void mem_prefetch_l2(const char* ptr, size_t num_lines) {
// The repeated fallthrough branches intentionally unroll up to 20 prefetches.
// NOLINTBEGIN(bugprone-branch-clone)
switch (num_lines) {
default:
[[fallthrough]];
case 20:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 19:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 18:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 17:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 16:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 15:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 14:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 13:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 12:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 11:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 10:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 9:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 8:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 7:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 6:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 5:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 4:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 3:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 2:
prefetch_l2(ptr);
ptr += 64;
[[fallthrough]];
case 1:
prefetch_l2(ptr);
[[fallthrough]];
case 0:
break;
}
// NOLINTEND(bugprone-branch-clone)
mem_prefetch_lines<prefetch_l2>(ptr, num_lines);
}
} // namespace rabitqlib::memory
Loading
Loading