Skip to content

Commit c43b3f0

Browse files
committed
Improve ACE GDS usage
- Fix wrongly-typed throughput calculation. - Keep sub_search_graph on GPU to use GDS.
1 parent 3c9ef03 commit c43b3f0

1 file changed

Lines changed: 99 additions & 46 deletions

File tree

cpp/src/neighbors/detail/cagra/cagra_build.cuh

Lines changed: 99 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <raft/core/host_mdspan.hpp>
1919
#include <raft/core/logger.hpp>
2020
#include <raft/core/resource/cuda_stream.hpp>
21+
#include <raft/util/cuda_rt_essentials.hpp>
2122
#include <raft/util/integer_utils.hpp>
2223

2324
#include <cuvs/cluster/kmeans.hpp>
@@ -429,35 +430,84 @@ void ace_adjust_sub_graph_ids(
429430
}
430431
}
431432

432-
// ACE: Adjust ids in sub search graph in place for disk version
433-
template <typename IdxT>
433+
// ACE: Adjust IDs into global reordered IDs on the device for the disk version.
434+
template <typename GraphIdxT, typename IdxT>
435+
__global__ void ace_adjust_sub_graph_ids_disk_kernel(const GraphIdxT* sub_search_graph,
436+
IdxT* adjusted_search_graph,
437+
size_t graph_edges,
438+
size_t core_sub_dataset_size,
439+
IdxT core_partition_offset,
440+
const IdxT* augmented_reordered_ids)
441+
{
442+
const size_t edge_id = static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
443+
if (edge_id >= graph_edges) { return; }
444+
445+
const GraphIdxT neighbor = sub_search_graph[edge_id];
446+
if (static_cast<size_t>(neighbor) < core_sub_dataset_size) {
447+
adjusted_search_graph[edge_id] = static_cast<IdxT>(neighbor) + core_partition_offset;
448+
} else {
449+
adjusted_search_graph[edge_id] =
450+
augmented_reordered_ids[static_cast<size_t>(neighbor) - core_sub_dataset_size];
451+
}
452+
}
453+
454+
template <typename IdxT, typename GraphIdxT>
434455
void ace_adjust_sub_graph_ids_disk(
456+
raft::resources const& res,
435457
size_t core_sub_dataset_size,
436458
size_t augmented_sub_dataset_size,
437459
size_t graph_degree,
438460
size_t partition_id,
439-
raft::host_matrix_view<IdxT, int64_t, raft::row_major> sub_search_graph,
461+
raft::device_matrix_view<const GraphIdxT, int64_t, raft::row_major> sub_search_graph,
462+
raft::device_matrix_view<IdxT, int64_t, raft::row_major> adjusted_search_graph,
440463
raft::host_vector_view<IdxT, int64_t, raft::row_major> core_partition_offsets,
441464
raft::host_vector_view<IdxT, int64_t, raft::row_major> augmented_partition_offsets,
442465
raft::host_vector_view<IdxT, int64_t, raft::row_major> augmented_backward_mapping,
443466
raft::host_vector_view<IdxT, int64_t, raft::row_major> core_forward_mapping)
444467
{
468+
RAFT_EXPECTS(static_cast<size_t>(sub_search_graph.extent(0)) >= core_sub_dataset_size,
469+
"ACE: source graph has fewer rows than the core partition");
470+
RAFT_EXPECTS(static_cast<size_t>(sub_search_graph.extent(1)) == graph_degree,
471+
"ACE: source graph degree does not match the requested graph degree");
472+
RAFT_EXPECTS(static_cast<size_t>(adjusted_search_graph.extent(0)) == core_sub_dataset_size &&
473+
static_cast<size_t>(adjusted_search_graph.extent(1)) == graph_degree,
474+
"ACE: adjusted graph shape does not match the core partition");
475+
476+
const size_t augmented_offset = augmented_partition_offsets(partition_id);
477+
RAFT_EXPECTS(augmented_offset + augmented_sub_dataset_size <=
478+
static_cast<size_t>(augmented_backward_mapping.extent(0)),
479+
"ACE: augmented partition exceeds the backward mapping");
480+
481+
// Resolve the global mapping once per augmented vector. This compact table is small enough to
482+
// copy to the GPU even when the full dataset mappings are too large to fit in device memory.
483+
auto augmented_reordered_ids = raft::make_host_vector<IdxT, int64_t>(augmented_sub_dataset_size);
445484
#pragma omp parallel for
446-
for (size_t i = 0; i < core_sub_dataset_size; i++) {
447-
for (size_t k = 0; k < graph_degree; k++) {
448-
size_t j = sub_search_graph(i, k);
449-
if (j < core_sub_dataset_size) {
450-
// core partition neighbor: local → core reordered
451-
sub_search_graph(i, k) = j + core_partition_offsets(partition_id);
452-
} else {
453-
// Augmented partition neighbor: local → augmented reordered→ original → core reordered
454-
size_t j_augmented = j - core_sub_dataset_size;
455-
size_t j_original =
456-
augmented_backward_mapping(j_augmented + augmented_partition_offsets(partition_id));
457-
sub_search_graph(i, k) = core_forward_mapping(j_original);
458-
}
459-
}
485+
for (size_t i = 0; i < augmented_sub_dataset_size; i++) {
486+
const size_t original_id = augmented_backward_mapping(augmented_offset + i);
487+
augmented_reordered_ids(i) = core_forward_mapping(original_id);
460488
}
489+
490+
auto augmented_reordered_ids_dev =
491+
raft::make_device_vector<IdxT, int64_t>(res, augmented_sub_dataset_size);
492+
raft::copy(res, augmented_reordered_ids_dev.view(), augmented_reordered_ids.view());
493+
494+
const size_t graph_edges = core_sub_dataset_size * graph_degree;
495+
if (graph_edges == 0) { return; }
496+
497+
constexpr uint32_t block_size = 256;
498+
const dim3 grid_size(raft::ceildiv(graph_edges, static_cast<size_t>(block_size)));
499+
ace_adjust_sub_graph_ids_disk_kernel<<<grid_size,
500+
block_size,
501+
0,
502+
raft::resource::get_cuda_stream(res)>>>(
503+
sub_search_graph.data_handle(),
504+
adjusted_search_graph.data_handle(),
505+
graph_edges,
506+
core_sub_dataset_size,
507+
core_partition_offsets(partition_id),
508+
augmented_reordered_ids_dev.data_handle());
509+
RAFT_CUDA_TRY(cudaPeekAtLastError());
510+
raft::resource::sync_stream(res);
461511
}
462512
463513
// ACE: Reorder dataset based on partition assignments and store to disk
@@ -1440,28 +1490,44 @@ index<T, IdxT> build_ace(raft::resources const& res,
14401490
auto optimize_elapsed =
14411491
std::chrono::duration_cast<std::chrono::milliseconds>(optimize_end - read_end).count();
14421492
1443-
// Copy graph edges for core members of this partition
1444-
auto sub_search_graph =
1445-
raft::make_host_matrix<IdxT, int64_t>(core_sub_dataset_size, graph_degree);
1446-
cudaStream_t stream = raft::resource::get_cuda_stream(res);
1447-
raft::copy(
1448-
res,
1449-
raft::make_host_vector_view(sub_search_graph.data_handle(), sub_search_graph.size()),
1450-
raft::make_device_vector_view(sub_index.graph().data_handle(), sub_search_graph.size()));
1451-
raft::resource::sync_stream(res, stream);
1452-
1493+
auto adjust_end = optimize_end;
1494+
auto write_elapsed = 0L;
1495+
const size_t graph_bytes = core_sub_dataset_size * graph_degree * sizeof(IdxT);
14531496
if (use_disk_mode) {
1454-
// Adjust IDs in sub_search_graph in place for disk storage
1455-
ace_adjust_sub_graph_ids_disk<IdxT>(core_sub_dataset_size,
1497+
// Keep the graph on the GPU, adjust IDs there, and write it directly through GDS.
1498+
auto adjusted_search_graph =
1499+
raft::make_device_matrix<IdxT, int64_t>(res, core_sub_dataset_size, graph_degree);
1500+
ace_adjust_sub_graph_ids_disk<IdxT>(res,
1501+
core_sub_dataset_size,
14561502
augmented_sub_dataset_size,
14571503
graph_degree,
14581504
partition_id,
1459-
sub_search_graph.view(),
1505+
sub_index.graph(),
1506+
adjusted_search_graph.view(),
14601507
core_partition_offsets.view(),
14611508
augmented_partition_offsets.view(),
14621509
augmented_backward_mapping.view(),
14631510
core_forward_mapping.view());
1511+
adjust_end = std::chrono::high_resolution_clock::now();
1512+
1513+
const size_t graph_offset =
1514+
static_cast<size_t>(core_partition_offsets(partition_id)) * graph_degree * sizeof(IdxT) +
1515+
graph_header_size;
1516+
cuvs::util::write_large_file(
1517+
graph_fd, adjusted_search_graph.data_handle(), graph_bytes, graph_offset);
1518+
auto write_end = std::chrono::high_resolution_clock::now();
1519+
write_elapsed =
1520+
std::chrono::duration_cast<std::chrono::milliseconds>(write_end - adjust_end).count();
14641521
} else {
1522+
// The in-memory result is host-resident, so copy the core graph back before remapping IDs.
1523+
auto sub_search_graph =
1524+
raft::make_host_matrix<IdxT, int64_t>(core_sub_dataset_size, graph_degree);
1525+
raft::copy(
1526+
res,
1527+
raft::make_host_vector_view(sub_search_graph.data_handle(), sub_search_graph.size()),
1528+
raft::make_device_vector_view(sub_index.graph().data_handle(), sub_search_graph.size()));
1529+
raft::resource::sync_stream(res);
1530+
14651531
// Adjust IDs in sub_search_graph and save to search_graph
14661532
ace_adjust_sub_graph_ids<IdxT>(core_sub_dataset_size,
14671533
augmented_sub_dataset_size,
@@ -1473,33 +1539,20 @@ index<T, IdxT> build_ace(raft::resources const& res,
14731539
augmented_partition_offsets.view(),
14741540
core_backward_mapping.view(),
14751541
augmented_backward_mapping.view());
1542+
adjust_end = std::chrono::high_resolution_clock::now();
14761543
}
14771544
1478-
auto adjust_end = std::chrono::high_resolution_clock::now();
14791545
auto adjust_elapsed =
14801546
std::chrono::duration_cast<std::chrono::milliseconds>(adjust_end - optimize_end).count();
14811547
1482-
if (use_disk_mode) {
1483-
const size_t graph_offset =
1484-
static_cast<size_t>(core_partition_offsets(partition_id)) * graph_degree * sizeof(IdxT) +
1485-
graph_header_size;
1486-
const size_t graph_bytes = core_sub_dataset_size * graph_degree * sizeof(IdxT);
1487-
cuvs::util::write_large_file(
1488-
graph_fd, sub_search_graph.data_handle(), graph_bytes, graph_offset);
1489-
}
1490-
1491-
auto end = std::chrono::high_resolution_clock::now();
1492-
auto write_elapsed =
1493-
std::chrono::duration_cast<std::chrono::milliseconds>(end - adjust_end).count();
1548+
auto end = std::chrono::high_resolution_clock::now();
14941549
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
14951550
double read_throughput =
14961551
read_elapsed > 0
14971552
? to_mib(sub_dataset_size * dataset_dim * sizeof(T)) / (read_elapsed / 1000.0)
14981553
: 0.0;
14991554
double write_throughput =
1500-
write_elapsed > 0
1501-
? to_mib(core_sub_dataset_size * dataset_dim * sizeof(T)) / (write_elapsed / 1000.0)
1502-
: 0.0;
1555+
write_elapsed > 0 ? to_mib(graph_bytes) / (write_elapsed / 1000.0) : 0.0;
15031556
RAFT_LOG_INFO(
15041557
"ACE: Partition %4lu (%8lu + %8lu) completed in %6ld ms: read %6ld ms (%7.1f MiB/s), "
15051558
"optimize %6ld ms, adjust %6ld ms, write %6ld ms (%7.1f MiB/s)",

0 commit comments

Comments
 (0)