Files
TheRock/rocm-systems/projects/rccl/test/SymmetricWindowMPITests.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

659 lines
21 KiB
C++

/*************************************************************************
* Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
*
* See LICENSE.txt for license information
************************************************************************/
/**
* @file SymmetricWindowMPITests.cpp
* @brief Tests for relaxed symmetric buffer registration (one-buffer path)
*
* Validates that symmetric kernels work correctly across different window
* registration patterns:
* - Both send and recv buffers registered (baseline)
* - Only one buffer registered (relaxed / one-buffer path)
* - No buffers registered (non-symmetric fallback)
* - In-place operations with a single window
*
* REQUIRED Environment Variables:
* NCCL_CUMEM_ENABLE=1 Enables cuMem API for symmetric support
* NCCL_DEBUG=INFO Enables debug logging to observe kernel path
* HSA_NO_SCRATCH_RECLAIM=1 Required for multi-GPU RCCL tests
*
* Run examples:
* mpirun -np 2 --bind-to none ./rccl-UnitTestsMPI --gtest_filter=SymWin_*
* mpirun -np 8 --bind-to none -x NCCL_DEBUG=INFO \
* ./rccl-UnitTestsMPI --gtest_filter=SymWin_AllReduce.*
*/
#include "DeviceBufferHelpers.hpp"
#include "MPITestBase.hpp"
#include "MPIHelpers.hpp"
#include "ResourceGuards.hpp"
#include "TestChecks.hpp"
#include <cstdlib>
#include <vector>
#ifdef MPI_TESTS_ENABLED
using namespace MPITestConstants;
using namespace RCCLTestGuards;
using namespace RCCLTestHelpers;
namespace {
constexpr size_t DEFAULT_COUNT = 256 * 1024;
constexpr int MIN_RANKS = 2;
}
// ============================================================================
// Base class for symmetric window tests
// ============================================================================
class SymmetricWindowTestBase : public MPITestBase
{
protected:
struct NcclBufInfo {
void* ptr = nullptr;
size_t size = 0;
};
struct WinInfo {
ncclWindow_t win = nullptr;
ncclComm_t comm = nullptr;
};
std::vector<NcclBufInfo> allocatedBufs_;
std::vector<WinInfo> registeredWins_;
void SetUp() override
{
MPITestBase::SetUp();
}
void TearDown() override
{
for (auto it = registeredWins_.rbegin(); it != registeredWins_.rend(); ++it) {
if (it->win && it->comm) {
ncclCommWindowDeregister(it->comm, it->win);
}
}
registeredWins_.clear();
for (auto it = allocatedBufs_.rbegin(); it != allocatedBufs_.rend(); ++it) {
if (it->ptr) {
ncclMemFree(it->ptr);
}
}
allocatedBufs_.clear();
MPITestBase::TearDown();
}
void* allocNcclBuf(size_t size)
{
void* ptr = nullptr;
ncclResult_t res = ncclMemAlloc(&ptr, size);
if (res != ncclSuccess || ptr == nullptr) return nullptr;
allocatedBufs_.push_back({ptr, size});
return ptr;
}
ncclWindow_t registerWindow(ncclComm_t comm, void* buf, size_t size,
int flags = NCCL_WIN_COLL_SYMMETRIC)
{
ncclWindow_t win = nullptr;
ncclResult_t res = ncclCommWindowRegister(comm, buf, size, &win, flags);
if (res != ncclSuccess) return nullptr;
registeredWins_.push_back({win, comm});
return win;
}
bool setupForSymmetric(int minRanks = MIN_RANKS)
{
const char* cuMemEnv = std::getenv("NCCL_CUMEM_ENABLE");
if (!cuMemEnv || std::string(cuMemEnv) != "1") return false;
if (!validateTestPrerequisites(minRanks)) return false;
if (createTestCommunicator() != ncclSuccess) return false;
// Verify ncclMemAlloc works (proxy check for VMM/symmetric support)
void* testBuf = nullptr;
ncclResult_t res = ncclMemAlloc(&testBuf, 4096);
if (res != ncclSuccess || testBuf == nullptr) return false;
ncclMemFree(testBuf);
return true;
}
template<typename T>
void initSendBuffer(void* buffer, size_t count, int rank)
{
ASSERT_MPI_EQ(hipSuccess, initializeBufferWithPattern<T>(buffer, count,
[rank](size_t) { return static_cast<T>(static_cast<float>(rank + 1)); }));
}
template<typename T>
bool checkAllReduceResult(void* buffer, size_t count, int nRanks)
{
T expected = static_cast<T>(static_cast<float>(nRanks * (nRanks + 1) / 2));
return verifyBufferData<T>(buffer, count, [expected](size_t) { return expected; });
}
template<typename T>
bool checkReduceScatterResult(void* buffer, size_t count, int nRanks)
{
T expected = static_cast<T>(static_cast<float>(nRanks * (nRanks + 1) / 2));
return verifyBufferData<T>(buffer, count, [expected](size_t) { return expected; });
}
template<typename T>
bool checkAllGatherResult(void* buffer, size_t countPerRank, int nRanks)
{
return verifyBufferData<T>(buffer, countPerRank * nRanks,
[countPerRank](size_t i) {
int srcRank = i / countPerRank;
return static_cast<T>(static_cast<float>(srcRank + 1));
});
}
};
// ============================================================================
// AllReduce with symmetric windows
// ============================================================================
class SymWin_AllReduce : public SymmetricWindowTestBase {};
TEST_F(SymWin_AllReduce, BothWindows_OutOfPlace)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t count = DEFAULT_COUNT;
const size_t bufSize = count * sizeof(T);
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
void* sendBuf = allocNcclBuf(bufSize);
void* recvBuf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
ncclWindow_t sendWin = registerWindow(comm, sendBuf, bufSize);
ncclWindow_t recvWin = registerWindow(comm, recvBuf, bufSize);
ASSERT_MPI_NE(sendWin, nullptr);
ASSERT_MPI_NE(recvWin, nullptr);
initSendBuffer<T>(sendBuf, count, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllReduce(sendBuf, recvBuf, count, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllReduceResult<T>(recvBuf, count, nRanks));
TEST_INFO("Rank %d: BothWindows_OutOfPlace passed", rank);
}
TEST_F(SymWin_AllReduce, OnlySendWindow_OutOfPlace)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t count = DEFAULT_COUNT;
const size_t bufSize = count * sizeof(T);
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
void* sendBuf = allocNcclBuf(bufSize);
void* recvBuf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
// Register ONLY send buffer
ncclWindow_t sendWin = registerWindow(comm, sendBuf, bufSize);
ASSERT_MPI_NE(sendWin, nullptr);
// recvBuf intentionally NOT registered
initSendBuffer<T>(sendBuf, count, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllReduce(sendBuf, recvBuf, count, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllReduceResult<T>(recvBuf, count, nRanks));
TEST_INFO("Rank %d: OnlySendWindow_OutOfPlace passed (relaxed path)", rank);
}
TEST_F(SymWin_AllReduce, OnlyRecvWindow_OutOfPlace)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t count = DEFAULT_COUNT;
const size_t bufSize = count * sizeof(T);
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
void* sendBuf = allocNcclBuf(bufSize);
void* recvBuf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
// Register ONLY recv buffer
// sendBuf intentionally NOT registered
ncclWindow_t recvWin = registerWindow(comm, recvBuf, bufSize);
ASSERT_MPI_NE(recvWin, nullptr);
initSendBuffer<T>(sendBuf, count, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllReduce(sendBuf, recvBuf, count, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllReduceResult<T>(recvBuf, count, nRanks));
TEST_INFO("Rank %d: OnlyRecvWindow_OutOfPlace passed (relaxed path)", rank);
}
TEST_F(SymWin_AllReduce, NoWindows_OutOfPlace)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t count = DEFAULT_COUNT;
const size_t bufSize = count * sizeof(T);
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
void* sendBuf = allocNcclBuf(bufSize);
void* recvBuf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
// No window registration — non-symmetric fallback path
initSendBuffer<T>(sendBuf, count, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllReduce(sendBuf, recvBuf, count, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllReduceResult<T>(recvBuf, count, nRanks));
TEST_INFO("Rank %d: NoWindows_OutOfPlace passed (non-symmetric fallback)", rank);
}
TEST_F(SymWin_AllReduce, SingleWindow_InPlace)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t count = DEFAULT_COUNT;
const size_t bufSize = count * sizeof(T);
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
void* buf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(buf, nullptr);
// Single window registration, in-place operation — primary one-buffer use case
ncclWindow_t win = registerWindow(comm, buf, bufSize);
ASSERT_MPI_NE(win, nullptr);
initSendBuffer<T>(buf, count, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllReduce(buf, buf, count, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllReduceResult<T>(buf, count, nRanks));
TEST_INFO("Rank %d: SingleWindow_InPlace passed (one-buffer path)", rank);
}
// ============================================================================
// ReduceScatter with symmetric windows
// ============================================================================
class SymWin_ReduceScatter : public SymmetricWindowTestBase {};
TEST_F(SymWin_ReduceScatter, BothWindows)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t countPerRank = DEFAULT_COUNT;
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
const size_t sendSize = countPerRank * nRanks * sizeof(T);
const size_t recvSize = countPerRank * sizeof(T);
void* sendBuf = allocNcclBuf(sendSize);
void* recvBuf = allocNcclBuf(recvSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
ncclWindow_t sendWin = registerWindow(comm, sendBuf, sendSize);
ncclWindow_t recvWin = registerWindow(comm, recvBuf, recvSize);
ASSERT_MPI_NE(sendWin, nullptr);
ASSERT_MPI_NE(recvWin, nullptr);
initSendBuffer<T>(sendBuf, countPerRank * nRanks, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclReduceScatter(sendBuf, recvBuf, countPerRank, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkReduceScatterResult<T>(recvBuf, countPerRank, nRanks));
TEST_INFO("Rank %d: ReduceScatter BothWindows passed", rank);
}
TEST_F(SymWin_ReduceScatter, OnlySendWindow)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t countPerRank = DEFAULT_COUNT;
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
const size_t sendSize = countPerRank * nRanks * sizeof(T);
const size_t recvSize = countPerRank * sizeof(T);
void* sendBuf = allocNcclBuf(sendSize);
void* recvBuf = allocNcclBuf(recvSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
// Only send buffer registered
ncclWindow_t sendWin = registerWindow(comm, sendBuf, sendSize);
ASSERT_MPI_NE(sendWin, nullptr);
initSendBuffer<T>(sendBuf, countPerRank * nRanks, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclReduceScatter(sendBuf, recvBuf, countPerRank, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkReduceScatterResult<T>(recvBuf, countPerRank, nRanks));
TEST_INFO("Rank %d: ReduceScatter OnlySendWindow passed (relaxed path)", rank);
}
TEST_F(SymWin_ReduceScatter, NoWindows)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t countPerRank = DEFAULT_COUNT;
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
const size_t sendSize = countPerRank * nRanks * sizeof(T);
const size_t recvSize = countPerRank * sizeof(T);
void* sendBuf = allocNcclBuf(sendSize);
void* recvBuf = allocNcclBuf(recvSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
initSendBuffer<T>(sendBuf, countPerRank * nRanks, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclReduceScatter(sendBuf, recvBuf, countPerRank, ncclFloat, ncclSum, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkReduceScatterResult<T>(recvBuf, countPerRank, nRanks));
TEST_INFO("Rank %d: ReduceScatter NoWindows passed (non-symmetric fallback)", rank);
}
// ============================================================================
// AllGather with symmetric windows
// ============================================================================
class SymWin_AllGather : public SymmetricWindowTestBase {};
TEST_F(SymWin_AllGather, BothWindows)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t countPerRank = DEFAULT_COUNT;
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
const size_t sendSize = countPerRank * sizeof(T);
const size_t recvSize = countPerRank * nRanks * sizeof(T);
void* sendBuf = allocNcclBuf(sendSize);
void* recvBuf = allocNcclBuf(recvSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
ncclWindow_t sendWin = registerWindow(comm, sendBuf, sendSize);
ncclWindow_t recvWin = registerWindow(comm, recvBuf, recvSize);
ASSERT_MPI_NE(sendWin, nullptr);
ASSERT_MPI_NE(recvWin, nullptr);
initSendBuffer<T>(sendBuf, countPerRank, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllGather(sendBuf, recvBuf, countPerRank, ncclFloat, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllGatherResult<T>(recvBuf, countPerRank, nRanks));
TEST_INFO("Rank %d: AllGather BothWindows passed", rank);
}
TEST_F(SymWin_AllGather, OnlyRecvWindow)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t countPerRank = DEFAULT_COUNT;
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
const size_t sendSize = countPerRank * sizeof(T);
const size_t recvSize = countPerRank * nRanks * sizeof(T);
void* sendBuf = allocNcclBuf(sendSize);
void* recvBuf = allocNcclBuf(recvSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
// Only recv buffer registered
ncclWindow_t recvWin = registerWindow(comm, recvBuf, recvSize);
ASSERT_MPI_NE(recvWin, nullptr);
initSendBuffer<T>(sendBuf, countPerRank, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllGather(sendBuf, recvBuf, countPerRank, ncclFloat, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllGatherResult<T>(recvBuf, countPerRank, nRanks));
TEST_INFO("Rank %d: AllGather OnlyRecvWindow passed (relaxed path)", rank);
}
TEST_F(SymWin_AllGather, NoWindows)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
using T = float;
const size_t countPerRank = DEFAULT_COUNT;
ncclComm_t comm = getActiveCommunicator();
hipStream_t stream = getActiveStream();
int rank, nRanks;
ncclCommUserRank(comm, &rank);
ncclCommCount(comm, &nRanks);
const size_t sendSize = countPerRank * sizeof(T);
const size_t recvSize = countPerRank * nRanks * sizeof(T);
void* sendBuf = allocNcclBuf(sendSize);
void* recvBuf = allocNcclBuf(recvSize);
ASSERT_MPI_NE(sendBuf, nullptr);
ASSERT_MPI_NE(recvBuf, nullptr);
initSendBuffer<T>(sendBuf, countPerRank, rank);
ASSERT_MPI_EQ(ncclSuccess,
ncclAllGather(sendBuf, recvBuf, countPerRank, ncclFloat, comm, stream));
ASSERT_EQ(hipSuccess, hipStreamSynchronize(stream));
ASSERT_TRUE(checkAllGatherResult<T>(recvBuf, countPerRank, nRanks));
TEST_INFO("Rank %d: AllGather NoWindows passed (non-symmetric fallback)", rank);
}
// ============================================================================
// Window lifecycle tests
// ============================================================================
class SymWin_WindowLifecycle : public SymmetricWindowTestBase {};
TEST_F(SymWin_WindowLifecycle, RegisterDeregister_Basic)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
ncclComm_t comm = getActiveCommunicator();
int rank;
ncclCommUserRank(comm, &rank);
const size_t bufSize = 1024 * 1024;
void* buf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(buf, nullptr);
ncclWindow_t win = registerWindow(comm, buf, bufSize);
ASSERT_MPI_NE(win, nullptr);
ASSERT_MPI_EQ(ncclSuccess, ncclCommWindowDeregister(comm, win));
// Null out tracked entry so TearDown skips double-deregister
registeredWins_.back().win = nullptr;
TEST_INFO("Rank %d: RegisterDeregister_Basic passed", rank);
}
TEST_F(SymWin_WindowLifecycle, MultipleWindows)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
ncclComm_t comm = getActiveCommunicator();
int rank;
ncclCommUserRank(comm, &rank);
const int numWindows = 4;
const size_t bufSize = 256 * 1024;
std::vector<ncclWindow_t> wins(numWindows);
for (int i = 0; i < numWindows; i++) {
void* buf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(buf, nullptr);
wins[i] = registerWindow(comm, buf, bufSize);
ASSERT_MPI_NE(wins[i], nullptr);
}
// Verify all windows are unique
for (int i = 0; i < numWindows; i++) {
for (int j = i + 1; j < numWindows; j++) {
ASSERT_MPI_NE(wins[i], wins[j]);
}
}
TEST_INFO("Rank %d: MultipleWindows passed (%d windows)", rank, numWindows);
}
TEST_F(SymWin_WindowLifecycle, RepeatedRegisterDeregister)
{
if (!setupForSymmetric()) {
GTEST_SKIP() << "Requires symmetric support with 2+ ranks";
}
ncclComm_t comm = getActiveCommunicator();
int rank;
ncclCommUserRank(comm, &rank);
const size_t bufSize = 512 * 1024;
void* buf = allocNcclBuf(bufSize);
ASSERT_MPI_NE(buf, nullptr);
const int iterations = 3;
for (int i = 0; i < iterations; i++) {
ncclWindow_t win = nullptr;
ASSERT_MPI_EQ(ncclSuccess,
ncclCommWindowRegister(comm, buf, bufSize, &win, NCCL_WIN_COLL_SYMMETRIC));
ASSERT_MPI_NE(win, nullptr);
ASSERT_MPI_EQ(ncclSuccess, ncclCommWindowDeregister(comm, win));
}
TEST_INFO("Rank %d: RepeatedRegisterDeregister passed (%d iterations)",
rank, iterations);
}
#endif // MPI_TESTS_ENABLED