[CCCL] 瘦身 + 补全: 移除 cudax/python/libcudacxx-tests 冗余文件, 新增 c2h 测试助手 + cmake 构建系统 + 8 个 CUDA thrust examples
变更摘要:
- 删除: cudax/ (783 files, 7.2M) — 实验性组件,竞赛不需要
- 删除: python/ (226 files, 2.0M) — Python 绑定,竞赛不需要
- 删除: libcudacxx/{test,benchmarks,codegen,cmake,share} (4432 files, 31M)
保留: libcudacxx/include/ (1463 headers, cuda::std 编译依赖)
- 新增: c2h/ (27 files) — CUB Catch2 测试辅助头文件,编译 243 个测试必需
- 新增: cmake/ (29 files) — CCCL 原生 CMake 构建系统
- 新增: thrust/examples/cuda/ (7 files) + cpp_integration/ (1 file)
async_reduce, custom_temporary_allocation, explicit_cuda_stream,
global_device_vector, range_view, unwrap_pointer, wrap_pointer, device
结果: cccl_upstream 从 74M→35M (瘦身 53%), 核心内容 100% 保留:
27/27 tuning headers, 78 benchmarks, 243 tests,
60 thrust examples, 18 CUB examples, 全部编译头文件
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
#===----------------------------------------------------------------------===##
|
||||
#
|
||||
# Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
# under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
#
|
||||
#===----------------------------------------------------------------------===##
|
||||
|
||||
add_subdirectory(nccl)
|
||||
@@ -1,20 +0,0 @@
|
||||
#===----------------------------------------------------------------------===##
|
||||
#
|
||||
# Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
# under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
#
|
||||
#===----------------------------------------------------------------------===##
|
||||
|
||||
if (NOT cudax_ENABLE_NCCL)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB test_srcs LIST_DIRECTORIES FALSE CONFIGURE_DEPENDS *.cu *.cpp)
|
||||
|
||||
foreach (src IN LISTS test_srcs)
|
||||
cudax_add_multi_gpu_test("communicators.nccl" test_target "${src}")
|
||||
target_link_libraries(${test_target} PRIVATE NCCL::nccl)
|
||||
endforeach()
|
||||
@@ -1,223 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cuda/devices>
|
||||
#include <cuda/std/type_traits>
|
||||
#include <cuda/std/utility>
|
||||
|
||||
#include <cuda/experimental/__multi_gpu/nccl_communicator.h>
|
||||
#include <cuda/experimental/__multi_gpu/nccl_communicator_ref.h>
|
||||
|
||||
#include <nccl.h>
|
||||
#include <nccl_test_common.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
[[nodiscard]] ncclComm_t make_nccl_communicator_handle()
|
||||
{
|
||||
if (cuda::devices.size() == 0)
|
||||
{
|
||||
SKIP("No CUDA devices visible");
|
||||
}
|
||||
|
||||
const int device = cuda::devices[0].get();
|
||||
ncclComm_t handle{};
|
||||
|
||||
const ncclResult_t result = ncclCommInitAll(&handle, 1, &device);
|
||||
|
||||
INFO("NCCL: " << ncclGetErrorString(result));
|
||||
REQUIRE(result == ncclSuccess);
|
||||
|
||||
return handle;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
C2H_TEST("nccl_communicator_ref typedefs", "[multi_gpu]")
|
||||
{
|
||||
STATIC_REQUIRE(::cuda::std::is_same_v<cudax::nccl_communicator_ref::native_handle_type, ncclComm_t>);
|
||||
STATIC_REQUIRE(
|
||||
::cuda::std::is_same_v<cudax::nccl_communicator_ref::group_guard_type,
|
||||
decltype(::cuda::std::declval<const cudax::nccl_communicator_ref&>().group_guard())>);
|
||||
}
|
||||
|
||||
C2H_TEST("nccl_communicator(s) not constructible from NCCL_COMM_NULL", "[multi_gpu]")
|
||||
{
|
||||
SECTION("ref")
|
||||
{
|
||||
STATIC_REQUIRE(!::cuda::std::is_constructible_v<cudax::nccl_communicator_ref, decltype(NCCL_COMM_NULL)>);
|
||||
STATIC_REQUIRE(!::cuda::std::is_constructible_v<cudax::nccl_communicator_ref, cuda::std::nullptr_t>);
|
||||
}
|
||||
|
||||
SECTION("owning")
|
||||
{
|
||||
STATIC_REQUIRE(!::cuda::std::is_constructible_v<cudax::nccl_communicator, decltype(NCCL_COMM_NULL)>);
|
||||
STATIC_REQUIRE(!::cuda::std::is_constructible_v<cudax::nccl_communicator, cuda::std::nullptr_t>);
|
||||
STATIC_REQUIRE(!::cuda::std::is_constructible_v<cudax::nccl_communicator, ncclComm_t>);
|
||||
}
|
||||
}
|
||||
|
||||
C2H_TEST("nccl_communicator basic", "[multi_gpu][nccl]")
|
||||
{
|
||||
SECTION("is move-only")
|
||||
{
|
||||
STATIC_REQUIRE(!cuda::std::is_copy_constructible_v<cudax::nccl_communicator>);
|
||||
STATIC_REQUIRE(!cuda::std::is_copy_assignable_v<cudax::nccl_communicator>);
|
||||
STATIC_REQUIRE(cuda::std::is_move_constructible_v<cudax::nccl_communicator>);
|
||||
STATIC_REQUIRE(cuda::std::is_nothrow_move_constructible_v<cudax::nccl_communicator>);
|
||||
STATIC_REQUIRE(cuda::std::is_move_assignable_v<cudax::nccl_communicator>);
|
||||
STATIC_REQUIRE(cuda::std::is_nothrow_move_assignable_v<cudax::nccl_communicator>);
|
||||
}
|
||||
|
||||
SECTION("factory construction")
|
||||
{
|
||||
STATIC_REQUIRE(
|
||||
cuda::std::is_same_v<decltype(cudax::nccl_communicator::from_native_handle(cuda::std::declval<ncclComm_t>())),
|
||||
cudax::nccl_communicator>);
|
||||
|
||||
//! [nccl_communicator_construction]
|
||||
const ncclComm_t handle = make_nccl_communicator_handle();
|
||||
|
||||
auto comm = cuda::experimental::nccl_communicator::from_native_handle(handle);
|
||||
|
||||
// comm owns the handle now
|
||||
REQUIRE(comm.native_handle() == handle);
|
||||
//! [nccl_communicator_construction]
|
||||
}
|
||||
|
||||
SECTION("factory construction with logical device")
|
||||
{
|
||||
STATIC_REQUIRE(
|
||||
cuda::std::is_same_v<decltype(cudax::nccl_communicator::from_native_handle(
|
||||
cuda::std::declval<ncclComm_t>(), cuda::std::declval<cudax::logical_device>())),
|
||||
cudax::nccl_communicator>);
|
||||
|
||||
//! [nccl_communicator_construction_with_logical_device]
|
||||
const ncclComm_t handle = make_nccl_communicator_handle();
|
||||
const auto device = cudax::logical_device{cuda::devices[0]};
|
||||
|
||||
auto comm = cudax::nccl_communicator::from_native_handle(handle, device);
|
||||
|
||||
REQUIRE(comm.native_handle() == handle);
|
||||
REQUIRE(comm.logical_device() == device);
|
||||
//! [nccl_communicator_construction_with_logical_device]
|
||||
}
|
||||
|
||||
SECTION("no_init construction")
|
||||
{
|
||||
STATIC_REQUIRE(cuda::std::is_nothrow_constructible_v<cudax::nccl_communicator, cuda::no_init_t>);
|
||||
|
||||
//! [nccl_communicator_no_init_construction]
|
||||
const auto comm = cudax::nccl_communicator{cuda::no_init};
|
||||
|
||||
REQUIRE(comm.native_handle() == ncclComm_t{NCCL_COMM_NULL});
|
||||
//! [nccl_communicator_no_init_construction]
|
||||
|
||||
REQUIRE(comm.rank() == 0);
|
||||
REQUIRE(comm.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("release")
|
||||
{
|
||||
//! [nccl_communicator_release]
|
||||
const ncclComm_t handle = make_nccl_communicator_handle();
|
||||
|
||||
auto comm = cuda::experimental::nccl_communicator::from_native_handle(handle);
|
||||
|
||||
const auto released_handle = comm.release();
|
||||
|
||||
// comm contains the null handle after release
|
||||
REQUIRE(comm.native_handle() == ncclComm_t{NCCL_COMM_NULL});
|
||||
REQUIRE(released_handle == handle);
|
||||
//! [nccl_communicator_release]
|
||||
|
||||
// so that we clean up properly
|
||||
[[maybe_unused]] const auto _ = cudax::nccl_communicator::from_native_handle(handle);
|
||||
}
|
||||
|
||||
SECTION("move construction")
|
||||
{
|
||||
//! [nccl_communicator_move_construction]
|
||||
const ncclComm_t handle = make_nccl_communicator_handle();
|
||||
|
||||
auto source = cudax::nccl_communicator::from_native_handle(handle);
|
||||
auto destination = cudax::nccl_communicator{cuda::std::move(source)};
|
||||
|
||||
// moved-from communicator is now invalid
|
||||
REQUIRE(source.native_handle() == ncclComm_t{NCCL_COMM_NULL});
|
||||
REQUIRE(destination.native_handle() == handle);
|
||||
//! [nccl_communicator_move_construction]
|
||||
}
|
||||
|
||||
SECTION("move assignment")
|
||||
{
|
||||
//! [nccl_communicator_move_assignment]
|
||||
auto source = cuda::experimental::nccl_communicator::from_native_handle(make_nccl_communicator_handle());
|
||||
auto destination = cuda::experimental::nccl_communicator::from_native_handle(make_nccl_communicator_handle());
|
||||
|
||||
// Save the native handle to verify that ownership is transferred.
|
||||
const auto handle = source.native_handle();
|
||||
|
||||
destination = cuda::std::move(source);
|
||||
|
||||
REQUIRE(source.native_handle() == ncclComm_t{NCCL_COMM_NULL});
|
||||
REQUIRE(destination.native_handle() == handle);
|
||||
//! [nccl_communicator_move_assignment]
|
||||
}
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref basic", )
|
||||
{
|
||||
SECTION("rank and size")
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (auto& comm : this->communicators())
|
||||
{
|
||||
REQUIRE(comm.rank() == i);
|
||||
REQUIRE(comm.size() == static_cast<int>(cuda::devices.size()));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("native handle")
|
||||
{
|
||||
for (auto& comm : this->communicators())
|
||||
{
|
||||
REQUIRE(comm.native_handle() != NCCL_COMM_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("logical device")
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (auto& comm : this->communicators())
|
||||
{
|
||||
REQUIRE(comm.logical_device().underlying_device() == cuda::devices[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("group_guard round trip")
|
||||
{
|
||||
// Opening and closing a guard with no enqueued ops must not throw.
|
||||
[[maybe_unused]] auto g = this->communicators().front().group_guard();
|
||||
}
|
||||
|
||||
SECTION("device mismatch throws")
|
||||
{
|
||||
if (cuda::devices.size() > 1)
|
||||
{
|
||||
REQUIRE_THROWS_WITH(
|
||||
cudax::nccl_communicator_ref(this->communicators()[0].native_handle(), cudax::logical_device{cuda::devices[1]}),
|
||||
"Inconsistent devices, NCCL communicator device and provided logical device do not match");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,487 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cuda/buffer>
|
||||
#include <cuda/devices>
|
||||
#include <cuda/functional>
|
||||
#include <cuda/memory_pool>
|
||||
#include <cuda/memory_resource>
|
||||
#include <cuda/std/cstddef>
|
||||
#include <cuda/std/cstdint>
|
||||
#include <cuda/std/functional>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <nccl_test_common.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr cuda::std::int32_t ROOT_RANK = 0;
|
||||
} // namespace
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref all_reduce sum", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
// Rank r contributes {r+1, r+1, r+1, r+1}; the element-wise sum is the n-th triangular number.
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
const auto values = {i + 1, i + 1, i + 1, i + 1};
|
||||
auto& s = send.emplace_back(streams[i], pool, values);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].all_reduce(
|
||||
g, send[i].data(), recv[i].data(), send[i].size(), ::cuda::std::plus<>{}, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
const auto sum = static_cast<cuda::std::int32_t>(cuda::devices.size() * (cuda::devices.size() + 1) / 2);
|
||||
|
||||
for (auto& buf : recv)
|
||||
{
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected = cuda::make_buffer(buf.stream(), pool, buf.size(), sum);
|
||||
const auto actual = cuda::make_buffer(buf.stream(), pool, buf);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref all_reduce maximum", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
const auto values = {i, 100 - i, 2 * i};
|
||||
auto& s = send.emplace_back(streams[i], pool, values);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].all_reduce(
|
||||
g, send[i].data(), recv[i].data(), send[i].size(), ::cuda::maximum<>{}, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
const auto n = static_cast<cuda::std::int32_t>(cuda::devices.size());
|
||||
|
||||
for (auto& buf : recv)
|
||||
{
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected_values = {n - 1, cuda::std::int32_t{100}, 2 * (n - 1)};
|
||||
const auto expected = cuda::make_buffer(buf.stream(), pool, expected_values);
|
||||
const auto actual = cuda::make_buffer(buf.stream(), pool, buf);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref reduce sum to root 0", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
const auto values = {i + 1, i + 1, i + 1, i + 1};
|
||||
auto& s = send.emplace_back(streams[i], pool, values);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].reduce(
|
||||
g, send[i].data(), recv[i].data(), send[i].size(), ::cuda::std::plus<>{}, ROOT_RANK, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
const auto sum = static_cast<cuda::std::int32_t>(cuda::devices.size() * (cuda::devices.size() + 1) / 2);
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto actual = cuda::make_buffer(recv[ROOT_RANK].stream(), pool, recv[ROOT_RANK]);
|
||||
const auto expected = cuda::make_buffer(actual.stream(), pool, actual.size(), sum);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref broadcast from root 0", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
// Only root's send buffer is read; give every rank the same literal so the source is obvious.
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
const auto values = {10, 20, 30, 40};
|
||||
auto& s = send.emplace_back(streams[i], pool, values);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].broadcast(g, send[i].data(), recv[i].data(), send[i].size(), ROOT_RANK, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
for (auto& buf : recv)
|
||||
{
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected_values = {
|
||||
cuda::std::int32_t{10}, cuda::std::int32_t{20}, cuda::std::int32_t{30}, cuda::std::int32_t{40}};
|
||||
const auto expected = cuda::make_buffer(buf.stream(), pool, expected_values);
|
||||
const auto actual = cuda::make_buffer(buf.stream(), pool, buf);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref all_gather", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
// Rank r contributes {10*r, 10*r+1}; every rank ends up with the concatenation in rank order.
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
const auto values = {10 * i, 10 * i + 1};
|
||||
auto& s = send.emplace_back(streams[i], pool, values);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size() * cuda::devices.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].all_gather(g, send[i].data(), recv[i].data(), send[i].size(), streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
const cuda::std::size_t per_rank = send.front().size();
|
||||
std::vector<cuda::std::int32_t> expected_values(per_rank * cuda::devices.size());
|
||||
|
||||
for (cuda::std::size_t r = 0; r < cuda::devices.size(); ++r)
|
||||
{
|
||||
expected_values[r * per_rank] = static_cast<cuda::std::int32_t>(10 * r);
|
||||
expected_values[(r * per_rank) + 1] = static_cast<cuda::std::int32_t>((10 * r) + 1);
|
||||
}
|
||||
|
||||
for (auto& buf : recv)
|
||||
{
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected = cuda::make_buffer<cuda::std::int32_t>(buf.stream(), pool, expected_values);
|
||||
const auto actual = cuda::make_buffer(buf.stream(), pool, buf);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref gather_v to root 0", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
// Rank r contributes (2 + r) elements: {10*r, 10*r+1, ...}. Root concatenates them in rank order.
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
|
||||
std::vector<cuda::std::int32_t> h(2 + static_cast<cuda::std::size_t>(i));
|
||||
|
||||
for (cuda::std::size_t k = 0; k < h.size(); ++k)
|
||||
{
|
||||
h[k] = (10 * i) + static_cast<int>(k);
|
||||
}
|
||||
|
||||
send.emplace_back(streams[i], pool, h);
|
||||
}
|
||||
|
||||
std::vector<cuda::std::size_t> recv_counts(cuda::devices.size());
|
||||
std::vector<cuda::std::size_t> displs(cuda::devices.size());
|
||||
cuda::std::size_t total = 0;
|
||||
|
||||
for (cuda::std::size_t r = 0; r < cuda::devices.size(); ++r)
|
||||
{
|
||||
recv_counts[r] = send[r].size();
|
||||
displs[r] = total;
|
||||
total += recv_counts[r];
|
||||
}
|
||||
|
||||
auto root_pool = cuda::device_default_memory_pool(cuda::devices[ROOT_RANK]);
|
||||
auto recv = cuda::make_buffer(streams[ROOT_RANK], root_pool, total, cuda::std::int32_t{-1});
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].gather_v(
|
||||
g, send[i].data(), send[i].size(), recv.data(), recv_counts.data(), displs.data(), ROOT_RANK, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
std::vector<cuda::std::int32_t> expected_values(total);
|
||||
|
||||
for (cuda::std::size_t r = 0; r < cuda::devices.size(); ++r)
|
||||
{
|
||||
for (cuda::std::size_t k = 0; k < recv_counts[r]; ++k)
|
||||
{
|
||||
expected_values[displs[r] + k] = static_cast<cuda::std::int32_t>((10 * r) + k);
|
||||
}
|
||||
}
|
||||
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected = cuda::make_buffer<cuda::std::int32_t>(recv.stream(), pool, expected_values);
|
||||
const auto actual = cuda::make_buffer(recv.stream(), pool, recv);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref all_to_all_v", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
// Two elements exchanged with each peer; the block for peer j sits at offset block*j.
|
||||
constexpr cuda::std::size_t block = 2;
|
||||
|
||||
std::vector<cuda::std::size_t> counts(cuda::devices.size(), block);
|
||||
std::vector<cuda::std::size_t> displs(cuda::devices.size());
|
||||
|
||||
for (cuda::std::size_t j = 0; j < cuda::devices.size(); ++j)
|
||||
{
|
||||
displs[j] = block * j;
|
||||
}
|
||||
|
||||
// Rank r block destined for peer j encodes 100*r + 10*j + k.
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
|
||||
std::vector<cuda::std::int32_t> h(block * cuda::devices.size());
|
||||
|
||||
for (cuda::std::size_t j = 0; j < cuda::devices.size(); ++j)
|
||||
{
|
||||
h[block * j] = static_cast<cuda::std::int32_t>((100 * i) + (10 * j));
|
||||
h[(block * j) + 1] = static_cast<cuda::std::int32_t>((100 * i) + (10 * j) + 1);
|
||||
}
|
||||
|
||||
auto& s = send.emplace_back(streams[i], pool, h);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].all_to_all_v(
|
||||
g, send[i].data(), counts.data(), displs.data(), recv[i].data(), counts.data(), displs.data(), streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
// Rank r receives from peer i the block i sent to r, placed at block*i: 100*i + 10*r + k.
|
||||
for (cuda::std::size_t r = 0; r < cuda::devices.size(); ++r)
|
||||
{
|
||||
std::vector<cuda::std::int32_t> expected_values(block * cuda::devices.size());
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
expected_values[block * i] = static_cast<cuda::std::int32_t>((100 * i) + (10 * r));
|
||||
expected_values[(block * i) + 1] = static_cast<cuda::std::int32_t>((100 * i) + (10 * r) + 1);
|
||||
}
|
||||
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected = cuda::make_buffer<cuda::std::int32_t>(recv[r].stream(), pool, expected_values);
|
||||
const auto actual = cuda::make_buffer(recv[r].stream(), pool, recv[r]);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
#if NCCL_VERSION_CODE >= NCCL_VERSION(2, 28, 0)
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref gather to root", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
const auto values = {10 * i, 10 * i + 1};
|
||||
auto& s = send.emplace_back(streams[i], pool, values);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size() * cuda::devices.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].gather(g, send[i].data(), recv[i].data(), send[i].size(), ROOT_RANK, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
const cuda::std::size_t per_rank = send.front().size();
|
||||
std::vector<cuda::std::int32_t> expected_values(per_rank * cuda::devices.size());
|
||||
|
||||
for (cuda::std::size_t r = 0; r < cuda::devices.size(); ++r)
|
||||
{
|
||||
expected_values[r * per_rank] = static_cast<cuda::std::int32_t>(10 * r);
|
||||
expected_values[r * per_rank + 1] = static_cast<cuda::std::int32_t>(10 * r + 1);
|
||||
}
|
||||
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected = cuda::make_buffer<cuda::std::int32_t>(recv[ROOT_RANK].stream(), pool, expected_values);
|
||||
const auto actual = cuda::make_buffer(recv[ROOT_RANK].stream(), pool, recv[ROOT_RANK]);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref all_to_all", )
|
||||
{
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
// Two elements exchanged with each peer; the block for peer j sits at offset block*j.
|
||||
constexpr cuda::std::size_t block = 2;
|
||||
|
||||
// Rank r block destined for peer j encodes 100*r + 10*j + k.
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(cuda::devices.size()); ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
|
||||
std::vector<cuda::std::int32_t> h(block * cuda::devices.size());
|
||||
|
||||
for (cuda::std::size_t j = 0; j < cuda::devices.size(); ++j)
|
||||
{
|
||||
h[block * j] = static_cast<cuda::std::int32_t>(100 * i + 10 * j);
|
||||
h[block * j + 1] = static_cast<cuda::std::int32_t>(100 * i + 10 * j + 1);
|
||||
}
|
||||
|
||||
auto& s = send.emplace_back(streams[i], pool, h);
|
||||
recv.emplace_back(cuda::make_buffer(streams[i], pool, s.size(), cuda::std::int32_t{-1}));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
this->communicators()[i].all_to_all(g, send[i].data(), recv[i].data(), block, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
// Rank r receives from peer i the block i sent to r, placed at block*i: 100*i + 10*r + k.
|
||||
for (cuda::std::size_t r = 0; r < cuda::devices.size(); ++r)
|
||||
{
|
||||
std::vector<cuda::std::int32_t> expected_values(block * cuda::devices.size());
|
||||
|
||||
for (cuda::std::size_t i = 0; i < cuda::devices.size(); ++i)
|
||||
{
|
||||
expected_values[block * i] = static_cast<cuda::std::int32_t>(100 * i + 10 * r);
|
||||
expected_values[block * i + 1] = static_cast<cuda::std::int32_t>(100 * i + 10 * r + 1);
|
||||
}
|
||||
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const auto expected = cuda::make_buffer<cuda::std::int32_t>(recv[r].stream(), pool, expected_values);
|
||||
const auto actual = cuda::make_buffer(recv[r].stream(), pool, recv[r]);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NCCL_VERSION_CODE >= NCCL_VERSION(2, 28, 0)
|
||||
@@ -1,92 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cuda/functional>
|
||||
#include <cuda/std/functional>
|
||||
|
||||
#include <cuda/experimental/__multi_gpu/concepts.h>
|
||||
#include <cuda/experimental/__multi_gpu/nccl_communicator_ref.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <testing.cuh>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct payload
|
||||
{
|
||||
int from;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct non_trivial
|
||||
{
|
||||
non_trivial(const non_trivial&) {} // NOLINT(modernize-use-equals-default)
|
||||
|
||||
int value;
|
||||
};
|
||||
|
||||
struct unsupported_op
|
||||
{};
|
||||
} // namespace
|
||||
|
||||
C2H_TEST("nccl_communicator_ref concept conformance", "[multi_gpu][nccl]")
|
||||
{
|
||||
STATIC_REQUIRE(cudax::__communicator<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(cudax::__has_send<cudax::nccl_communicator_ref, int*>);
|
||||
STATIC_REQUIRE(cudax::__has_send<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_send<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
STATIC_REQUIRE(cudax::__has_recv<cudax::nccl_communicator_ref, int*>);
|
||||
STATIC_REQUIRE(cudax::__has_recv<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_recv<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
STATIC_REQUIRE(cudax::__has_reduce<cudax::nccl_communicator_ref, int*>);
|
||||
STATIC_REQUIRE(!cudax::__has_reduce<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(cudax::__has_all_reduce<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(!cudax::__has_all_reduce<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(cudax::__has_gather<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(cudax::__has_gather<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_gather<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
STATIC_REQUIRE(cudax::__has_gather_v<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(cudax::__has_gather_v<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_gather_v<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
STATIC_REQUIRE(cudax::__has_all_gather<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(cudax::__has_all_gather<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_all_gather<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
STATIC_REQUIRE(cudax::__has_broadcast<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(cudax::__has_broadcast<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_broadcast<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
STATIC_REQUIRE(cudax::__has_all_to_all<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(cudax::__has_all_to_all<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_all_to_all<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
STATIC_REQUIRE(cudax::__has_all_to_all_v<cudax::nccl_communicator_ref>);
|
||||
STATIC_REQUIRE(cudax::__has_all_to_all_v<cudax::nccl_communicator_ref, payload*>);
|
||||
STATIC_REQUIRE(!cudax::__has_all_to_all_v<cudax::nccl_communicator_ref, non_trivial*>);
|
||||
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<int>);
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<int*>);
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<const int*>);
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<const volatile int* const>);
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<float>);
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<::cuda::std::int32_t>);
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<void>);
|
||||
STATIC_REQUIRE(cudax::nccl_transportable<payload>);
|
||||
STATIC_REQUIRE(!cudax::nccl_transportable<non_trivial>);
|
||||
|
||||
STATIC_REQUIRE(cudax::nccl_reducible<int, cuda::std::plus<>>);
|
||||
STATIC_REQUIRE(cudax::nccl_reducible<int, cuda::std::multiplies<>>);
|
||||
STATIC_REQUIRE(cudax::nccl_reducible<int, cuda::maximum<>>);
|
||||
STATIC_REQUIRE(cudax::nccl_reducible<int, cuda::minimum<>>);
|
||||
STATIC_REQUIRE(!cudax::nccl_reducible<payload, cuda::std::plus<>>);
|
||||
STATIC_REQUIRE(!cudax::nccl_reducible<int, unsupported_op>);
|
||||
|
||||
STATIC_REQUIRE(cudax::nccl_reducible<int, std::plus<>>);
|
||||
STATIC_REQUIRE(cudax::nccl_reducible<int, std::multiplies<>>);
|
||||
STATIC_REQUIRE(!cudax::nccl_reducible<payload, std::plus<>>);
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Libraries,
|
||||
// under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cuda/buffer>
|
||||
#include <cuda/devices>
|
||||
#include <cuda/memory_pool>
|
||||
#include <cuda/memory_resource>
|
||||
#include <cuda/std/cstdint>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <nccl_test_common.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct payload
|
||||
{
|
||||
cuda::std::int32_t from;
|
||||
cuda::std::int32_t index;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Ring exchange via send/recv. Rank r contributes {r, r, r}.
|
||||
MULTI_GPU_TEST("nccl_communicator_ref send/recv ring", )
|
||||
{
|
||||
if (cuda::devices.size() == 1)
|
||||
{
|
||||
// NCCL disallows self send/recv on a 1-rank comm.
|
||||
REQUIRE(this->communicators().front().rank() == 0);
|
||||
REQUIRE(this->communicators().front().size() == 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const int n = static_cast<int>(cuda::devices.size());
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> send;
|
||||
std::vector<cuda::device_buffer<cuda::std::int32_t>> recv;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
|
||||
auto& s = send.emplace_back(cuda::make_buffer(streams[i], pool, 3, i));
|
||||
recv.emplace_back(cuda::make_buffer<cuda::std::int32_t>(streams[i], pool, s.size(), -1));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
const int prev = (i + n - 1) % n;
|
||||
const int next = (i + 1) % n;
|
||||
|
||||
this->communicators()[i].recv(g, recv[i].data(), recv[i].size(), prev, streams[i]);
|
||||
this->communicators()[i].send(g, send[i].data(), send[i].size(), next, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
// Rank r received from its predecessor (r-1): {r-1, r-1, r-1}.
|
||||
for (int r = 0; r < n; ++r)
|
||||
{
|
||||
const cuda::std::int32_t prev = (r + n - 1) % n;
|
||||
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const cuda::host_buffer<cuda::std::int32_t> expected =
|
||||
cuda::make_buffer(recv[r].stream(), pool, recv[r].size(), prev);
|
||||
const cuda::host_buffer<cuda::std::int32_t> actual = cuda::make_buffer(recv[r].stream(), pool, recv[r]);
|
||||
|
||||
REQUIRE_THAT(actual, Equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
MULTI_GPU_TEST("nccl_communicator_ref send/recv transports trivially copyable payload", )
|
||||
{
|
||||
if (cuda::devices.size() == 1)
|
||||
{
|
||||
// NCCL disallows self send/recv on a 1-rank comm.
|
||||
REQUIRE(this->communicators().front().rank() == 0);
|
||||
REQUIRE(this->communicators().front().size() == 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const int n = static_cast<int>(cuda::devices.size());
|
||||
auto streams = nccl_test_util::make_streams();
|
||||
|
||||
std::vector<cuda::device_buffer<payload>> send;
|
||||
std::vector<cuda::device_buffer<payload>> recv;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto pool = cuda::device_default_memory_pool(cuda::devices[i]);
|
||||
|
||||
std::vector<payload> h(3);
|
||||
|
||||
for (cuda::std::size_t k = 0; k < h.size(); ++k)
|
||||
{
|
||||
h[k] = payload{static_cast<cuda::std::int32_t>(i), static_cast<cuda::std::int32_t>(k)};
|
||||
}
|
||||
|
||||
auto& s = send.emplace_back(streams[i], pool, h);
|
||||
recv.emplace_back(cuda::make_buffer<payload>(streams[i], pool, s.size(), cuda::no_init));
|
||||
}
|
||||
|
||||
{
|
||||
auto g = this->communicators().front().group_guard();
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
const int prev = (i + n - 1) % n;
|
||||
const int next = (i + 1) % n;
|
||||
|
||||
this->communicators()[i].recv(g, recv[i].data(), recv[i].size(), prev, streams[i]);
|
||||
this->communicators()[i].send(g, send[i].data(), send[i].size(), next, streams[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& stream : streams)
|
||||
{
|
||||
stream.sync();
|
||||
}
|
||||
|
||||
for (int r = 0; r < n; ++r)
|
||||
{
|
||||
const int prev = (r + n - 1) % n;
|
||||
|
||||
auto pool = cuda::mr::legacy_pinned_memory_resource{};
|
||||
const cuda::host_buffer<payload> actual = cuda::make_buffer(recv[r].stream(), pool, recv[r]);
|
||||
|
||||
actual.stream().sync();
|
||||
for (cuda::std::size_t k = 0; k < actual.size(); ++k)
|
||||
{
|
||||
REQUIRE(actual[k].from == static_cast<cuda::std::int32_t>(prev));
|
||||
REQUIRE(actual[k].index == static_cast<cuda::std::int32_t>(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user