Skip to content
Open
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
11 changes: 7 additions & 4 deletions c/src/neighbors/brute_force.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

/*
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -250,10 +250,13 @@ extern "C" cuvsError_t cuvsBruteForceDeserialize(cuvsResources_t res,
index->dtype.bits = dtype.itemsize * 8;
if (dtype.kind == 'f' && dtype.itemsize == 4) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_deserialize<float>(res, filename));
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
index->addr =
reinterpret_cast<uintptr_t>(_deserialize<float>(res, filename));
} else if ((dtype.kind == 'f' || dtype.kind == 'e') &&
dtype.itemsize == 2) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_deserialize<half>(res, filename));
index->addr =
reinterpret_cast<uintptr_t>(_deserialize<half>(res, filename));
} else {
RAFT_FAIL("Unsupported index dtype: %d and bits: %d", index->dtype.code, index->dtype.bits);
}
Expand Down
2 changes: 1 addition & 1 deletion c/src/neighbors/cagra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,7 @@ static auto read_serialized_header(cuvsResources_t res, const char *filename)
.code = 0, .bits = static_cast<uint8_t>(dtype.itemsize * 8), .lanes = 1};
if (dtype.kind == 'f' && dtype.itemsize == 4) {
output_dtype.code = kDLFloat;
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
} else if ((dtype.kind == 'f' || dtype.kind == 'e') && dtype.itemsize == 2) {
output_dtype.code = kDLFloat;
} else if (dtype.kind == 'i' && dtype.itemsize == 1) {
output_dtype.code = kDLInt;
Expand Down
11 changes: 7 additions & 4 deletions c/src/neighbors/ivf_flat.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

/*
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -310,10 +310,13 @@ extern "C" cuvsError_t cuvsIvfFlatDeserialize(cuvsResources_t res,

index->dtype.bits = dtype.itemsize * 8;
if (dtype.kind == 'f' && dtype.itemsize == 4) {
index->addr = reinterpret_cast<uintptr_t>(_deserialize<float, int64_t>(res, filename));
index->addr = reinterpret_cast<uintptr_t>(
_deserialize<float, int64_t>(res, filename));
index->dtype.code = kDLFloat;
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
index->addr = reinterpret_cast<uintptr_t>(_deserialize<half, int64_t>(res, filename));
} else if ((dtype.kind == 'f' || dtype.kind == 'e') &&
dtype.itemsize == 2) {
index->addr = reinterpret_cast<uintptr_t>(
_deserialize<half, int64_t>(res, filename));
index->dtype.code = kDLFloat;
index->dtype.bits = 16;
} else if (dtype.kind == 'i' && dtype.itemsize == 1) {
Expand Down
4 changes: 2 additions & 2 deletions c/src/neighbors/mg_cagra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ extern "C" cuvsError_t cuvsMultiGpuCagraDeserialize(cuvsResources_t res,
if (dtype.kind == 'f' && dtype.itemsize == 4) {
index->dtype.code = kDLFloat;
index->addr = try_layout_deser(float{});
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
} else if ((dtype.kind == 'f' || dtype.kind == 'e') && dtype.itemsize == 2) {
index->dtype.code = kDLFloat;
index->addr = try_layout_deser(half{});
} else if (dtype.kind == 'i' && dtype.itemsize == 1) {
Expand Down Expand Up @@ -627,7 +627,7 @@ extern "C" cuvsError_t cuvsMultiGpuCagraDistribute(cuvsResources_t res,
if (dtype.kind == 'f' && dtype.itemsize == 4) {
index->dtype.code = kDLFloat;
index->addr = try_layout_distribute(float{});
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
} else if ((dtype.kind == 'f' || dtype.kind == 'e') && dtype.itemsize == 2) {
index->dtype.code = kDLFloat;
index->addr = try_layout_distribute(half{});
} else if (dtype.kind == 'i' && dtype.itemsize == 1) {
Expand Down
20 changes: 13 additions & 7 deletions c/src/neighbors/mg_ivf_flat.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -408,10 +408,13 @@ extern "C" cuvsError_t cuvsMultiGpuIvfFlatDeserialize(cuvsResources_t res,
index->dtype.bits = dtype.itemsize * 8;
if (dtype.kind == 'f' && dtype.itemsize == 4) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_mg_deserialize<float>(res, filename));
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
index->addr =
reinterpret_cast<uintptr_t>(_mg_deserialize<float>(res, filename));
} else if ((dtype.kind == 'f' || dtype.kind == 'e') &&
dtype.itemsize == 2) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_mg_deserialize<half>(res, filename));
index->addr =
reinterpret_cast<uintptr_t>(_mg_deserialize<half>(res, filename));
} else if (dtype.kind == 'i' && dtype.itemsize == 1) {
index->dtype.code = kDLInt;
index->addr = reinterpret_cast<uintptr_t>(_mg_deserialize<int8_t>(res, filename));
Expand Down Expand Up @@ -442,10 +445,13 @@ extern "C" cuvsError_t cuvsMultiGpuIvfFlatDistribute(cuvsResources_t res,
index->dtype.bits = dtype.itemsize * 8;
if (dtype.kind == 'f' && dtype.itemsize == 4) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_mg_distribute<float>(res, filename));
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
index->addr =
reinterpret_cast<uintptr_t>(_mg_distribute<float>(res, filename));
} else if ((dtype.kind == 'f' || dtype.kind == 'e') &&
dtype.itemsize == 2) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_mg_distribute<half>(res, filename));
index->addr =
reinterpret_cast<uintptr_t>(_mg_distribute<half>(res, filename));
} else if (dtype.kind == 'i' && dtype.itemsize == 1) {
index->dtype.code = kDLInt;
index->addr = reinterpret_cast<uintptr_t>(_mg_distribute<int8_t>(res, filename));
Expand Down
11 changes: 7 additions & 4 deletions c/src/neighbors/mg_ivf_pq.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -400,10 +400,13 @@ extern "C" cuvsError_t cuvsMultiGpuIvfPqDeserialize(cuvsResources_t res,
index->dtype.bits = dtype.itemsize * 8;
if (dtype.kind == 'f' && dtype.itemsize == 4) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_mg_deserialize<float>(res, filename));
} else if (dtype.kind == 'e' && dtype.itemsize == 2) {
index->addr =
reinterpret_cast<uintptr_t>(_mg_deserialize<float>(res, filename));
} else if ((dtype.kind == 'f' || dtype.kind == 'e') &&
dtype.itemsize == 2) {
index->dtype.code = kDLFloat;
index->addr = reinterpret_cast<uintptr_t>(_mg_deserialize<half>(res, filename));
index->addr =
reinterpret_cast<uintptr_t>(_mg_deserialize<half>(res, filename));
} else if (dtype.kind == 'i' && dtype.itemsize == 1) {
index->dtype.code = kDLInt;
index->addr = reinterpret_cast<uintptr_t>(_mg_deserialize<int8_t>(res, filename));
Expand Down
61 changes: 59 additions & 2 deletions cpp/bench/ann/src/cuvs/cuvs_cagra_hnswlib.cu
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#include "../common/ann_types.hpp"
#include "../common/conf.hpp"
#include "cuvs_ann_bench_param_parser.h"
#include "cuvs_cagra_hnswlib_wrapper.h"

Expand Down Expand Up @@ -33,15 +34,71 @@ auto parse_build_param(const nlohmann::json& conf) ->
} else {
hnsw_params.hierarchy = cuvs::neighbors::hnsw::HnswHierarchy::GPU;
}
if (conf.contains("output_format")) {
if (conf.at("output_format") == "hnswlib") {
hnsw_params.output_format = cuvs::neighbors::hnsw::HnswOutputFormat::HNSWLIB;
} else if (conf.at("output_format") == "graph_only") {
hnsw_params.output_format = cuvs::neighbors::hnsw::HnswOutputFormat::GRAPH_ONLY;
} else {
THROW("Invalid value for output_format: %s",
conf.at("output_format").get<std::string>().c_str());
}
}
if (conf.contains("ef_construction")) {
hnsw_params.ef_construction = conf.at("ef_construction");
}
if (conf.contains("dataset_path")) {
param.dataset_path = conf.at("dataset_path");
} else if (hnsw_params.output_format == cuvs::neighbors::hnsw::HnswOutputFormat::GRAPH_ONLY) {
param.dataset_path = configuration::singleton().get_dataset_conf().base_file;
}
if (conf.contains("num_threads")) { hnsw_params.num_threads = conf.at("num_threads"); }

// Reuse the CAGRA wrapper params parser
::parse_build_param<T, IdxT>(conf, cagra_params);

if (conf.contains("M")) { hnsw_params.M = conf.at("M"); }

// ACE / GRAPH_ONLY builds can be fine-tuned from the benchmark config. The library
// auto-selects the build algorithm from `M` and `ef_construction`; here we only forward the
// explicit ACE overrides (if any) onto the new hnsw index params.
auto ace_conf = collect_conf_with_prefix(conf, "ace_");
if (!ace_conf.empty()) {
auto ace_params = cuvs::neighbors::hnsw::graph_build_params::ace_params();
if (ace_conf.contains("npartitions")) { ace_params.npartitions = ace_conf.at("npartitions"); }
if (ace_conf.contains("build_dir")) { ace_params.build_dir = ace_conf.at("build_dir"); }
if (ace_conf.contains("ef_construction")) {
ace_params.ef_construction = ace_conf.at("ef_construction");
}
if (ace_conf.contains("use_disk")) { ace_params.use_disk = ace_conf.at("use_disk"); }
hnsw_params.graph_build_params = ace_params;
}

// GRAPH_ONLY always needs disk-backed ACE settings before hnsw::build.
if (hnsw_params.output_format == cuvs::neighbors::hnsw::HnswOutputFormat::GRAPH_ONLY) {
auto ace_params = std::holds_alternative<cuvs::neighbors::hnsw::graph_build_params::ace_params>(
hnsw_params.graph_build_params)
? std::get<cuvs::neighbors::hnsw::graph_build_params::ace_params>(
hnsw_params.graph_build_params)
: cuvs::neighbors::hnsw::graph_build_params::ace_params();
if (!ace_conf.contains("use_disk")) { ace_params.use_disk = true; }
const auto use_disk_conf =
ace_conf.contains("use_disk") ? ace_conf.at("use_disk").dump() : std::string{"unset"};
const auto build_dir_conf =
ace_conf.contains("build_dir") ? ace_conf.at("build_dir").dump() : std::string{"unset"};
RAFT_EXPECTS(ace_params.use_disk,
"GRAPH_ONLY requires ACE disk mode (ace_params.use_disk = true); "
"got ace_use_disk=%s",
use_disk_conf.c_str());
RAFT_EXPECTS(!ace_params.build_dir.empty(),
"GRAPH_ONLY requires ace_params.build_dir to be set; "
"got ace_build_dir=%s",
build_dir_conf.c_str());
RAFT_EXPECTS(!param.dataset_path.empty(),
"GRAPH_ONLY requires dataset_path or a configured dataset base_file; "
"got dataset_path='%s'",
param.dataset_path.c_str());
hnsw_params.graph_build_params = ace_params;
}
return param;
}

Expand Down
61 changes: 49 additions & 12 deletions cpp/bench/ann/src/cuvs/cuvs_cagra_hnswlib_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,34 @@
#include <raft/core/logger.hpp>

#include <chrono>
#include <filesystem>
#include <memory>
#include <string>

namespace cuvs::bench {

inline void move_file_overwrite(const std::filesystem::path& src, const std::filesystem::path& dst)
{
std::error_code ec;
if (src == dst ||
(std::filesystem::exists(dst, ec) && std::filesystem::equivalent(src, dst, ec))) {
return;
}
if (!dst.parent_path().empty()) { std::filesystem::create_directories(dst.parent_path()); }
if (std::filesystem::exists(dst, ec)) { std::filesystem::remove(dst, ec); }

std::filesystem::rename(src, dst, ec);
if (ec) {
// Rename fails across filesystems. Fall back to copy followed by removal of the source.
ec.clear();
std::filesystem::copy_file(src, dst, std::filesystem::copy_options::overwrite_existing, ec);
const auto src_str = src.string();
const auto dst_str = dst.string();
RAFT_EXPECTS(!ec, "Failed to move '%s' to '%s'.", src_str.c_str(), dst_str.c_str());
std::filesystem::remove(src, ec);
}
}

template <typename T, typename IdxT>
class cuvs_cagra_hnswlib : public algo<T>, public algo_gpu {
public:
Expand All @@ -22,6 +46,7 @@ class cuvs_cagra_hnswlib : public algo<T>, public algo_gpu {
using cagra_wrapper_params = typename cuvs_cagra<T, IdxT>::build_param;
cagra_wrapper_params cagra_build_params;
cuvs::neighbors::hnsw::index_params hnsw_index_params;
std::string dataset_path;
};

struct search_param : public search_param_base {
Expand Down Expand Up @@ -97,18 +122,25 @@ void cuvs_cagra_hnswlib<T, IdxT>::set_search_param(const search_param_base& para
template <typename T, typename IdxT>
void cuvs_cagra_hnswlib<T, IdxT>::save(const std::string& file) const
{
if (build_param_.hnsw_index_params.output_format ==
cuvs::neighbors::hnsw::HnswOutputFormat::GRAPH_ONLY) {
const auto src_artifact = std::filesystem::path(hnsw_index_->file_path());
RAFT_EXPECTS(!src_artifact.empty(), "Layered HNSW artifact path is not available.");
RAFT_EXPECTS(std::filesystem::exists(src_artifact),
"Layered HNSW artifact '%s' does not exist.",
src_artifact.c_str());

move_file_overwrite(src_artifact, std::filesystem::path(file));
return;
}

if (cagra_ace_build_) {
std::string index_filename = hnsw_index_->file_path();
RAFT_EXPECTS(!index_filename.empty(), "HNSW index file path is not available.");
RAFT_EXPECTS(std::filesystem::exists(index_filename),
"Index file '%s' does not exist.",
index_filename.c_str());
if (std::filesystem::exists(file)) { std::filesystem::remove(file); }
// might fail when using 2 different filesystems
std::error_code ec;
std::filesystem::rename(index_filename, file, ec);
RAFT_EXPECTS(
!ec, "Failed to rename index file '%s' to '%s'.", index_filename.c_str(), file.c_str());
move_file_overwrite(std::filesystem::path(index_filename), std::filesystem::path(file));
} else {
cuvs::neighbors::hnsw::serialize(handle_, file, *(hnsw_index_.get()));
}
Expand All @@ -118,12 +150,17 @@ template <typename T, typename IdxT>
void cuvs_cagra_hnswlib<T, IdxT>::load(const std::string& file)
{
cuvs::neighbors::hnsw::index<T>* idx = nullptr;
cuvs::neighbors::hnsw::deserialize(handle_,
build_param_.hnsw_index_params,
file,
this->dim_,
parse_metric_type(this->metric_),
&idx);
if (build_param_.hnsw_index_params.output_format ==
cuvs::neighbors::hnsw::HnswOutputFormat::GRAPH_ONLY) {
cuvs::neighbors::hnsw::deserialize(handle_, file, build_param_.dataset_path, &idx);
} else {
cuvs::neighbors::hnsw::deserialize(handle_,
build_param_.hnsw_index_params,
file,
this->dim_,
parse_metric_type(this->metric_),
&idx);
}
hnsw_index_ = std::shared_ptr<cuvs::neighbors::hnsw::index<T>>(idx);
}

Expand Down
Loading
Loading