Skip to content
Open
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
dd8b301
Scale native allreduce/allgather algos for MNNVL/MNNVLS
Binyang2014 Apr 27, 2026
893a08e
Enable MNNVL allreduce tuning
Binyang2014 Apr 28, 2026
dded5e0
Improve MNNVL allreduce tuning performance
Binyang2014 Apr 28, 2026
865c2bc
Optimize MNNVL allreduce without symmetric memory
Binyang2014 Apr 28, 2026
3bc00cb
Enable NVLS zero-copy without symmetric memory flag
Binyang2014 Apr 28, 2026
533f329
Tune no-sym MNNVL with RSAG zero-copy
Binyang2014 Apr 28, 2026
45a651b
Decouple IPC-domain hint from bootstrap nRanksPerNode
Binyang2014 May 1, 2026
2a2fca8
Rename collective ctx/kernel param nRanksPerNode to ipcDomainNranks
Binyang2014 May 1, 2026
2efda4d
Restore compile-time templated NRanksPerNode for rsag_zero_copy
Binyang2014 May 1, 2026
1c29817
Revert AllreduceRsAgZeroCopy non-symmetric ctx key tag back to ++tag
Binyang2014 May 1, 2026
7bc5e04
Reset GPU tokens before reuse
Binyang2014 May 2, 2026
9a36884
Rename gpuMemset wrapper and zero TokenPool slots in deleter
Binyang2014 May 2, 2026
987f800
Merge remote-tracking branch 'origin/main' into binyli/mnnvl
Binyang2014 May 4, 2026
6296803
Make NVLS non-zero-copy allreduce algorithms MNNVL-ready
Binyang2014 May 5, 2026
9aeeaf0
Simplify torch-integration tuning example for MPI-only multi-node tes…
Binyang2014 May 6, 2026
905b23d
Drop non-MNNVL multi_node regime from torch-integration example
Binyang2014 May 6, 2026
4a0d5b2
Simplify torch-integration tuning example
Binyang2014 May 6, 2026
307a471
Shorten verbose comments and use THROW in validateIpcDomainSpansWorld
Binyang2014 May 6, 2026
f0c6ac0
Fold validateIpcDomainSpansWorld into getIpcDomainNranks
Binyang2014 May 6, 2026
bde23ce
Revert verbose RSAG zero-copy comment; rename NRanksPerNode template …
Binyang2014 May 6, 2026
095cfff
Revert RSAG nBlocks default to 64
Binyang2014 May 6, 2026
639b80d
Tie AllreduceAllpairPacket maxBlockNum_ to MAX_IPC_DOMAIN_NRANKS - 1
Binyang2014 May 6, 2026
e8caab7
Strip preflight validation blocks from NVLS pipeline allreduce kernels
Binyang2014 May 6, 2026
7d80a33
Default torch example SYMMETRIC_MEMORY env to 1
Binyang2014 May 6, 2026
d1b04a3
NVLS zero-copy allreduce: support FP16 accumulator for FP8 inputs
Binyang2014 May 7, 2026
113d859
fix
Binyang2014 May 8, 2026
9ff7e1c
update
Binyang2014 May 8, 2026
654bcfa
update
Binyang2014 May 8, 2026
5516bdb
fix
Binyang2014 May 8, 2026
e208cc3
WIP
Binyang2014 May 8, 2026
825fc12
address hang issue
Binyang2014 May 9, 2026
224b3de
Clean up completed communicator receives
Binyang2014 May 13, 2026
0c09239
Merge branch 'main' into binyli/mnnvl
Binyang2014 May 13, 2026
7724e49
Fix lint and ROCm error alias
Copilot May 13, 2026
24850ef
Merge branch 'main' into binyli/mnnvl
Binyang2014 May 15, 2026
ee82cc4
Merge branch 'main' into binyli/mnnvl
Binyang2014 May 15, 2026
dbebde2
Configure IPC domain per communicator
Binyang2014 May 15, 2026
93b4354
temp solution
Binyang2014 May 15, 2026
0744e80
detect ipc domain automaticlly
Binyang2014 May 16, 2026
94af88d
Fix tuning example hang
Binyang2014 May 16, 2026
f32cfb1
update
Binyang2014 May 16, 2026
594dc79
Address NVLS review feedback
seagater May 16, 2026
18d3737
Tighten NVML IPC domain hash lookup
seagater May 16, 2026
4db71b9
Move barrier into setupNvlsChannels and clean up NVLS pipeline state
Binyang2014 May 18, 2026
35331cf
Fix collective topology sizing
Binyang2014 May 20, 2026
ac44e98
update
Binyang2014 May 20, 2026
7308c32
merge main
Binyang2014 May 22, 2026
42ece40
Fix memory leak
Binyang2014 May 24, 2026
641420d
increase nvls memory size to 64 GB
Binyang2014 May 26, 2026
ea73a1e
WIP
Binyang2014 May 26, 2026
ba0b3e3
merge main
Binyang2014 Jun 4, 2026
99f20cd
merge main
Binyang2014 Jun 4, 2026
ecc8c5c
move tuner to benchmark
Binyang2014 Jun 4, 2026
000cd5b
update algo
Binyang2014 Jun 5, 2026
cd9d504
add check for nvls fp8 support
Binyang2014 Jun 5, 2026
d754c35
lint and log
Binyang2014 Jun 6, 2026
2954d81
log
Binyang2014 Jun 6, 2026
d401f97
WIP
Binyang2014 Jun 6, 2026
ee3606e
WIP
Binyang2014 Jun 6, 2026
e1c2679
add more logs
Binyang2014 Jun 8, 2026
0a26c30
for buffer pool
Binyang2014 Jun 12, 2026
ddf8b14
update for buffer pool
Binyang2014 Jun 13, 2026
9c26eb4
add test
Binyang2014 Jun 15, 2026
b4030f1
Merge origin/main into binyli/mnnvl
Binyang2014 Jun 30, 2026
6924259
wip
caiocbr Jul 24, 2026
cd4c660
Merge main into binyli/mnnvl
Binyang2014 Jul 26, 2026
644fa6e
Merge origin/main into binyli/mnnvl
Binyang2014 Jul 28, 2026
5d79947
update macro
Binyang2014 Jul 28, 2026
7cc49ee
code clean-up
Binyang2014 Jul 28, 2026
695ce28
Merge branch 'main' into binyli/mnnvl
Binyang2014 Aug 13, 2026
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
6 changes: 3 additions & 3 deletions include/mscclpp/gpu_data_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ using __bfloat162 = __nv_bfloat162;

#endif

// Defined when the current device compilation target supports FP8 (`e4m3`/`e5m2`) multimem
// instructions.
#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 1000) && \
(defined(__CUDA_ARCH_SPECIFIC__) || defined(__CUDA_ARCH_FAMILY_SPECIFIC__))
#define MSCCLPP_DEVICE_FP8_MULTIMEM_SUPPORTED
#define MSCCLPP_DEVICE_FP8_MULTIMEM_SUPPORTED 1
#else
#define MSCCLPP_DEVICE_FP8_MULTIMEM_SUPPORTED 0
#endif

/// Software float8 with 4 exponent bits, 3 mantissa bits, exponent bias = 15.
Expand Down
93 changes: 92 additions & 1 deletion include/mscclpp/gpu_utils.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// Licensed under the MIT License.

#ifndef MSCCLPP_GPU_UTILS_HPP_
#define MSCCLPP_GPU_UTILS_HPP_

#include <memory>
#include <optional>
#include <unordered_map>

#include "env.hpp"
Expand Down Expand Up @@ -403,6 +404,96 @@ class GpuBuffer {
std::shared_ptr<T> memory_;
};

namespace detail {

class GpuBufferPoolStorage;

} // namespace detail

/// A deterministic sub-allocation pool for GPU communication buffers.
///
/// The pool allocates one `GpuBuffer<char>` slab and returns reference-counted buffers for sub-ranges in that slab.
/// If all ranks create pools with the same size and make the same allocation calls, returned buffers have identical
/// offsets from the slab base pointer. Python bindings use this property to build symmetric memory buffers while
/// relying on normal Python reference counting to return buffers to the local pool.
///
class GpuBufferPool {
public:
/// Represents one buffer returned by `GpuBufferPool`.
///
/// This object owns a sub-range of a pool allocation. Destroying the last reference to this object returns the
/// sub-range to the pool. Destruction only updates local pool bookkeeping and does not perform any
/// cross-rank synchronization.
class Buffer {
Comment thread
Binyang2014 marked this conversation as resolved.
Outdated
public:
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
Buffer(Buffer&&) = delete;
Buffer& operator=(Buffer&&) = delete;

/// Destructor. Returns this buffer's sub-range to the owning pool when the last reference is destroyed.
~Buffer();

/// Returns the number of bytes requested for this buffer.
/// @return Number of bytes in this buffer.
size_t bytes() const;

/// Returns this buffer's byte offset from the pool base pointer.
/// @return Byte offset from the pool base pointer.
size_t offset() const;

/// Returns the device pointer to this buffer.
/// @return Device pointer to this buffer.
char* data() const;

/// Returns the device id of the underlying pool allocation.
/// @return Device id of the underlying pool allocation.
int deviceId() const;

private:
friend class detail::GpuBufferPoolStorage;
Buffer(std::shared_ptr<detail::GpuBufferPoolStorage> storage, size_t offset, size_t bytes);

std::shared_ptr<detail::GpuBufferPoolStorage> storage_;
size_t offset_;
size_t bytes_;
};

/// Constructs a pool backed by a single `GpuBuffer<char>`.
/// @param bytes Number of bytes to reserve in the pool.
/// @param granularity Granularity used to size the underlying `GpuBuffer`.
explicit GpuBufferPool(size_t bytes, GpuBufferGranularity granularity = GpuBufferGranularity::MultiCastMinimum);

/// Allocates a sub-range from the pool.
/// @param bytes Number of bytes to allocate.
/// @param alignment Alignment in bytes for the returned offset.
/// @return A reference-counted pooled buffer.
std::shared_ptr<Buffer> allocate(size_t bytes, size_t alignment = 256);

/// Returns the number of bytes in the underlying pool allocation.
/// @return Number of bytes in the pool.
size_t bytes() const;

/// Returns the number of bytes that are available for new buffers.
/// @return Number of free bytes.
size_t freeBytes() const;

/// Returns the number of bytes currently held by active pooled buffers.
/// @return Number of active bytes.
size_t activeBytes() const;

/// Returns the device pointer to the pool base.
/// @return Device pointer to the pool base.
char* data();

/// Returns the device id of the underlying pool allocation.
/// @return Device id of the underlying pool allocation.
int deviceId() const;

private:
std::shared_ptr<detail::GpuBufferPoolStorage> storage_;
};

} // namespace mscclpp

#endif // MSCCLPP_GPU_UTILS_HPP_
4 changes: 2 additions & 2 deletions include/mscclpp/switch_channel_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ struct SwitchChannelDeviceHandle {
: "l"(ptr)
: "memory");
}
#if defined(MSCCLPP_DEVICE_FP8_MULTIMEM_SUPPORTED)
#if MSCCLPP_DEVICE_FP8_MULTIMEM_SUPPORTED
else if constexpr (std::is_same_v<VectorType, f8_e4m3x4>) {
if constexpr (std::is_same_v<AccumT, __half>) {
asm("multimem.ld_reduce.relaxed.sys.global.add.acc::f16.e4m3x4 %0, [%1];"
Expand Down Expand Up @@ -198,7 +198,7 @@ struct SwitchChannelDeviceHandle {
"r"(val.words[1]), "r"(val.words[2]), "r"(val.words[3])
: "memory");
}
#if defined(MSCCLPP_DEVICE_FP8_MULTIMEM_SUPPORTED)
#if MSCCLPP_DEVICE_FP8_MULTIMEM_SUPPORTED
else if constexpr (std::is_same_v<VectorType, f8_e4m3x4>) {
asm volatile("multimem.st.relaxed.sys.global.e4m3x4 [%0], %1;" ::"l"(ptr), "r"(val.words[0]) : "memory");
} else if constexpr (std::is_same_v<VectorType, f8_e4m3x8>) {
Expand Down
138 changes: 90 additions & 48 deletions python/csrc/gpu_utils_py.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// Licensed under the MIT License.

#include <dlpack/dlpack.h>
#include <nanobind/nanobind.h>
#include <nanobind/stl/optional.h>
#include <nanobind/stl/shared_ptr.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/vector.h>

#include <memory>
#include <mscclpp/gpu_data_types.hpp>
#include <mscclpp/gpu_utils.hpp>

Expand All @@ -15,6 +17,13 @@ using namespace mscclpp;

constexpr int BYTE_BITS = 8;

struct DlpackContext {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there value in creating this struct just for makeDlpack instead of just temp variables within the function?

DLManagedTensor managedTensor{};
std::vector<int64_t> shape;
std::vector<int64_t> strides;
std::shared_ptr<void> owner;
};

static DLDeviceType getDeviceType() {
#if defined(MSCCLPP_USE_ROCM)
return kDLROCM;
Expand Down Expand Up @@ -52,65 +61,77 @@ static DLDataType getDlType(std::string type) {
}
}

static nb::capsule toDlpack(GpuBuffer<char> buffer, std::string dataType, std::vector<int64_t>& shape,
std::vector<int64_t>& strides) {
static void dlpackDeleter(DLManagedTensor* self) { delete static_cast<DlpackContext*>(self->manager_ctx); }
Comment thread
Binyang2014 marked this conversation as resolved.
Outdated

static void dlpackCapsuleDestructor(PyObject* capsule) {
if (PyCapsule_IsValid(capsule, "used_dltensor")) {
return;
}
if (!PyCapsule_IsValid(capsule, "dltensor")) {
return;
}
DLManagedTensor* managedTensor = static_cast<DLManagedTensor*>(PyCapsule_GetPointer(capsule, "dltensor"));
if (managedTensor == nullptr) {
return;
}
if (managedTensor->deleter) {
managedTensor->deleter(managedTensor);
}
}

static nb::capsule makeDlpack(void* data, size_t bytes, int deviceId, std::shared_ptr<void> owner, std::string dataType,
std::vector<int64_t> shape, std::vector<int64_t> strides) {
DLDataType dtype = getDlType(dataType);
int64_t* tensorShape = shape.size() > 0 ? new int64_t[shape.size()] : new int64_t[1];
int64_t* tensorStrides = strides.size() > 0 ? new int64_t[strides.size()] : nullptr;
if (shape.size() == 0) {
tensorShape[0] = (int64_t)(buffer.nelems() / ((dtype.bits * dtype.lanes + 7) / BYTE_BITS));
} else {
for (size_t i = 0; i < shape.size(); ++i) {
tensorShape[i] = shape[i];
auto ctx = std::make_unique<DlpackContext>();
size_t elementBytes = (dtype.bits * dtype.lanes + 7) / BYTE_BITS;
if (shape.empty()) {
if (bytes % elementBytes != 0) {
throw Error("DLPack buffer size must be divisible by the element size.", ErrorCode::InvalidUsage);
}
ctx->shape.push_back((int64_t)(bytes / elementBytes));
} else {
ctx->shape = std::move(shape);
}
for (size_t i = 0; i < strides.size(); ++i) {
tensorStrides[i] = strides[i];
ctx->strides = std::move(strides);
if (!ctx->strides.empty() && ctx->strides.size() != ctx->shape.size()) {
throw Error("DLPack strides must have the same length as shape.", ErrorCode::InvalidUsage);
}
ctx->owner = std::move(owner);

DLManagedTensor* dlManagedTensor = new DLManagedTensor();
dlManagedTensor->dl_tensor.data = buffer.data();
DLManagedTensor* dlManagedTensor = &ctx->managedTensor;
dlManagedTensor->dl_tensor.data = data;
dlManagedTensor->dl_tensor.device.device_type = getDeviceType();
dlManagedTensor->dl_tensor.device.device_id = buffer.deviceId();
dlManagedTensor->dl_tensor.ndim = shape.size() == 0 ? 1 : shape.size();
dlManagedTensor->dl_tensor.strides = tensorStrides;
dlManagedTensor->dl_tensor.shape = tensorShape;
dlManagedTensor->dl_tensor.device.device_id = deviceId;
dlManagedTensor->dl_tensor.ndim = static_cast<int>(ctx->shape.size());
dlManagedTensor->dl_tensor.strides = ctx->strides.empty() ? nullptr : ctx->strides.data();
dlManagedTensor->dl_tensor.shape = ctx->shape.data();
dlManagedTensor->dl_tensor.byte_offset = 0;
dlManagedTensor->dl_tensor.dtype = dtype;
dlManagedTensor->manager_ctx = new GpuBuffer<char>(buffer);
dlManagedTensor->deleter = [](DLManagedTensor* self) {
delete static_cast<GpuBuffer<char>*>(self->manager_ctx);
self->manager_ctx = nullptr;
self->dl_tensor.data = nullptr;
if (self->dl_tensor.shape != nullptr) {
delete[] self->dl_tensor.shape;
self->dl_tensor.shape = nullptr;
if (self->dl_tensor.strides) {
delete[] self->dl_tensor.strides;
self->dl_tensor.strides = nullptr;
}
}
delete self;
};
dlManagedTensor->manager_ctx = ctx.get();
dlManagedTensor->deleter = dlpackDeleter;

PyObject* dlCapsule = PyCapsule_New(static_cast<void*>(dlManagedTensor), "dltensor", [](PyObject* capsule) {
if (PyCapsule_IsValid(capsule, "used_dltensor")) {
return;
}
if (!PyCapsule_IsValid(capsule, "dltensor")) {
return;
}
DLManagedTensor* managedTensor = static_cast<DLManagedTensor*>(PyCapsule_GetPointer(capsule, "dltensor"));
if (managedTensor == nullptr) {
return;
}
if (managedTensor->deleter) {
managedTensor->deleter(managedTensor);
}
});
PyObject* dlCapsule = PyCapsule_New(static_cast<void*>(dlManagedTensor), "dltensor", dlpackCapsuleDestructor);
if (dlCapsule == nullptr) {
throw Error("Failed to create DLPack capsule.", ErrorCode::InvalidUsage);
}
ctx.release();
return nb::steal<nb::capsule>(dlCapsule);
}

static nb::capsule toDlpack(GpuBuffer<char> buffer, std::string dataType, std::vector<int64_t>& shape,
std::vector<int64_t>& strides) {
auto owner = std::make_shared<GpuBuffer<char>>(buffer);
return makeDlpack(buffer.data(), buffer.nelems(), buffer.deviceId(), std::move(owner), dataType, shape, strides);
}

static nb::capsule toDlpack(std::shared_ptr<GpuBufferPool::Buffer> buffer, std::string dataType,
std::vector<int64_t>& shape, std::vector<int64_t>& strides) {
void* data = buffer->data();
size_t bytes = buffer->bytes();
int deviceId = buffer->deviceId();
return makeDlpack(data, bytes, deviceId, std::move(buffer), dataType, shape, strides);
}
Comment thread
Binyang2014 marked this conversation as resolved.
Outdated

void register_gpu_utils(nb::module_& m) {
m.def("is_nvls_supported", &isNvlsSupported);

Expand All @@ -131,4 +152,25 @@ void register_gpu_utils(nb::module_& m) {
return toDlpack(self, dataType, shape, strides);
},
nb::arg("data_type"), nb::arg("shape") = std::vector<int64_t>(), nb::arg("strides") = std::vector<int64_t>());

nb::class_<GpuBufferPool::Buffer>(m, "CppRawGpuBufferPoolBuffer")
.def("bytes", &GpuBufferPool::Buffer::bytes)
.def("offset", &GpuBufferPool::Buffer::offset)
.def("data", [](GpuBufferPool::Buffer& self) { return reinterpret_cast<uintptr_t>(self.data()); })
.def("device_id", &GpuBufferPool::Buffer::deviceId)
.def(
"to_dlpack",
[](std::shared_ptr<GpuBufferPool::Buffer> self, std::string dataType, std::vector<int64_t> shape,
std::vector<int64_t> strides) { return toDlpack(std::move(self), dataType, shape, strides); },
nb::arg("data_type"), nb::arg("shape") = std::vector<int64_t>(), nb::arg("strides") = std::vector<int64_t>());

nb::class_<GpuBufferPool>(m, "CppRawGpuBufferPool")
.def(nb::init<size_t, GpuBufferGranularity>(), nb::arg("bytes"),
nb::arg("granularity") = GpuBufferGranularity::MultiCastMinimum)
.def("bytes", &GpuBufferPool::bytes)
.def("free_bytes", &GpuBufferPool::freeBytes)
.def("active_bytes", &GpuBufferPool::activeBytes)
.def("data", [](GpuBufferPool& self) { return reinterpret_cast<uintptr_t>(self.data()); })
.def("device_id", &GpuBufferPool::deviceId)
.def("allocate", &GpuBufferPool::allocate, nb::arg("bytes"), nb::arg("alignment") = 256);
}
5 changes: 5 additions & 0 deletions python/mscclpp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
CppExecutionPlan as ExecutionPlan,
CppPacketType as PacketType,
CppRawGpuBuffer as RawGpuBuffer,
CppRawGpuBufferPool as RawGpuBufferPool,
CppRawGpuBufferPoolBuffer as RawGpuBufferPoolBuffer,
CppReduceOp as ReduceOp,
env,
is_nvls_supported,
Expand Down Expand Up @@ -84,6 +86,8 @@
"ExecutionPlan",
"PacketType",
"RawGpuBuffer",
"RawGpuBufferPool",
"RawGpuBufferPoolBuffer",
Comment thread
Binyang2014 marked this conversation as resolved.
Outdated
"ReduceOp",
"env",
"version",
Expand All @@ -100,6 +104,7 @@
"AlgorithmCollection",
"CommGroup",
"GpuBuffer",
"GpuBufferPool",
"GpuBufferGranularity",
]

Expand Down
Loading
Loading