This is an automated email from the ASF dual-hosted git repository.

yongwww pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 2f889774ec [3rdparty] AUTO mode for custom all-reduce strategy (#16797)
2f889774ec is described below

commit 2f889774ec10b56ebfac89f78698e06eb200db46
Author: Ruihang Lai <[email protected]>
AuthorDate: Wed Mar 27 01:30:09 2024 -0400

    [3rdparty] AUTO mode for custom all-reduce strategy (#16797)
    
    This PR adds the automatic mode selection for customized all-reduce
    kernels, referring TensorRT-LLM.
    
    Meanwhile, this PR fixes a bug that may cause customized all-reduce
    kernel to hang forever. Prior to this PR, each worker resets its
    barrier values to 0 *after using all-gather to exchange their
    barrier handles*. Afterwards, the customized all-reduce kernels
    update the barriers of all workers. So it is possible that, worker 0
    updates worker 1's barrier *before* worker 1 resets its barrier to 0.
    This lead to the all-reduce kernel hanging forever.
    
    This PR changes the behavior to resetting barriers before all-gather,
    and forcing a device synchronization after reset.
---
 3rdparty/tensorrt_llm/custom_allreduce_kernels.h   | 33 ++++++++++++++++++++++
 .../tvm/relax/transform/ipc_allreduce_rewrite.py   |  2 --
 src/runtime/disco/cuda_ipc/cuda_ipc_memory.cc      | 26 +++++++++++------
 src/runtime/disco/cuda_ipc/custom_allreduce.cc     | 12 ++++++--
 tests/python/disco/test_custom_allreduce.py        |  4 +++
 5 files changed, 63 insertions(+), 14 deletions(-)

diff --git a/3rdparty/tensorrt_llm/custom_allreduce_kernels.h 
b/3rdparty/tensorrt_llm/custom_allreduce_kernels.h
index 7fd66e5d10..7c515a03ac 100644
--- a/3rdparty/tensorrt_llm/custom_allreduce_kernels.h
+++ b/3rdparty/tensorrt_llm/custom_allreduce_kernels.h
@@ -25,8 +25,10 @@ constexpr size_t MAX_RANKS_PER_NODE = 8;
 constexpr size_t DEFAULT_BLOCK_SIZE = 1024;
 
 enum class AllReduceStrategyType : int8_t {
+  RING = 0,
   ONESHOT = 1,
   TWOSHOT = 2,
+  AUTO = 3,
 };
 
 struct AllReduceParams {
@@ -42,6 +44,37 @@ struct AllReduceParams {
   void* local_output_buffer_ptr;
 };
 
+inline size_t GetMaxRequiredWorkspaceSize(int world_size) {
+  if (world_size <= 2) {
+    return 16 * 1000 * 1000;
+  }
+  return 8 * 1000 * 1000;
+}
+
+inline AllReduceStrategyType SelectImplementation(size_t message_size, int 
world_size) {
+  const size_t maxWorkspaceSize = GetMaxRequiredWorkspaceSize(world_size);
+
+  if (message_size > maxWorkspaceSize) {
+    return AllReduceStrategyType::RING;
+  }
+
+  if (world_size <= 2) {
+    return AllReduceStrategyType::ONESHOT;
+  }
+
+  if (world_size <= 4) {
+    if (message_size < 1 * 1000 * 1000) {
+      return AllReduceStrategyType::ONESHOT;
+    }
+    return AllReduceStrategyType::TWOSHOT;
+  }
+
+  if (message_size < 500 * 1000) {
+    return AllReduceStrategyType::ONESHOT;
+  }
+  return AllReduceStrategyType::TWOSHOT;
+}
+
 void customAllReduce(AllReduceParams& params, void* data, size_t elts, 
DLDataType dataType,
                      AllReduceStrategyType strat, cudaStream_t stream);
 
diff --git a/python/tvm/relax/transform/ipc_allreduce_rewrite.py 
b/python/tvm/relax/transform/ipc_allreduce_rewrite.py
index 3e7b005a60..df40181cb9 100644
--- a/python/tvm/relax/transform/ipc_allreduce_rewrite.py
+++ b/python/tvm/relax/transform/ipc_allreduce_rewrite.py
@@ -40,8 +40,6 @@ class IPCAllReduceRewrite:
             The all-reduce strategy. Only "1" and "2" are supported.
             "1" stands for one-shot, and "2" stands for two-shot.
         """
-        if allreduce_strategy not in [1, 2]:
-            raise ValueError(f"All-reduce strategy {allreduce_strategy} is not 
supported.")
         self.allreduce_strategy = allreduce_strategy
 
     def transform_module(self, mod: IRModule, _ctx: tvm.transform.PassContext) 
-> IRModule:
diff --git a/src/runtime/disco/cuda_ipc/cuda_ipc_memory.cc 
b/src/runtime/disco/cuda_ipc/cuda_ipc_memory.cc
index 451c3df0cb..fec5abec86 100644
--- a/src/runtime/disco/cuda_ipc/cuda_ipc_memory.cc
+++ b/src/runtime/disco/cuda_ipc/cuda_ipc_memory.cc
@@ -91,15 +91,13 @@ class CUDAIPCMemoryAllocator final : public 
memory::PooledAllocator {
  private:
   void* DeviceAllocDataSpace(Device dev, size_t size, size_t alignment,
                              DLDataType type_hint) final {
-    auto [data_ptr, data_comm_ptrs] = AllocIPCMemory(dev, size, alignment, 
type_hint);
+    auto [data_ptr, data_comm_ptrs] =
+        AllocIPCMemory(dev, size, alignment, type_hint, 
/*reset_memory_to_zero=*/false);
     int barrier_ptr_size = sizeof(uint32_t) * (MAX_ALL_REDUCE_BLOCKS + 2) * 
MAX_RANKS_PER_NODE;
-    auto [barrier_in_ptr, barrier_in_comm_ptrs] =
-        AllocIPCMemory(dev, barrier_ptr_size, alignment, DataType::UInt(32));
-    auto [barrier_out_ptr, barrier_out_comm_ptrs] =
-        AllocIPCMemory(dev, barrier_ptr_size, alignment, DataType::UInt(32));
-    // Initialize the barrier values to 0 to avoid synchronization issue.
-    CUDA_CALL(cudaMemset(barrier_in_ptr, 0, barrier_ptr_size));
-    CUDA_CALL(cudaMemset(barrier_out_ptr, 0, barrier_ptr_size));
+    auto [barrier_in_ptr, barrier_in_comm_ptrs] = AllocIPCMemory(
+        dev, barrier_ptr_size, alignment, DataType::UInt(32), 
/*reset_memory_to_zero=*/true);
+    auto [barrier_out_ptr, barrier_out_comm_ptrs] = AllocIPCMemory(
+        dev, barrier_ptr_size, alignment, DataType::UInt(32), 
/*reset_memory_to_zero=*/true);
 
     // Create the CUDAIPCMemory object.
     ObjectPtr<CUDAIPCMemoryObj> ipc_memory = make_object<CUDAIPCMemoryObj>();
@@ -142,12 +140,22 @@ class CUDAIPCMemoryAllocator final : public 
memory::PooledAllocator {
    * pointer.
    */
   std::pair<void*, std::vector<void*>> AllocIPCMemory(Device dev, size_t size, 
size_t alignment,
-                                                      DLDataType type_hint) {
+                                                      DLDataType type_hint,
+                                                      bool 
reset_memory_to_zero) {
     // Alloc local buffer
     ICHECK(dev.device_type == kDLCUDA);
     void* ptr;
     CUDA_CALL(cudaSetDevice(dev.device_id));
     CUDA_CALL(cudaMalloc(&ptr, size));
+    // Reset allocated memory to zero when required.
+    // We explicitly synchronize after memset, to make sure memset finishes
+    // before using all-gather to exchange IPC handles.
+    // This is important to ensure the memory reset get ordered
+    // before any other peers read the memory.
+    if (reset_memory_to_zero) {
+      CUDA_CALL(cudaMemset(ptr, 0, size));
+      CUDA_CALL(cudaDeviceSynchronize());
+    }
     // Create ipc handle
     cudaIpcMemHandle_t local_handle;
     CUDA_CALL(cudaIpcGetMemHandle(&local_handle, ptr));
diff --git a/src/runtime/disco/cuda_ipc/custom_allreduce.cc 
b/src/runtime/disco/cuda_ipc/custom_allreduce.cc
index e9be5973e1..98fd777b83 100644
--- a/src/runtime/disco/cuda_ipc/custom_allreduce.cc
+++ b/src/runtime/disco/cuda_ipc/custom_allreduce.cc
@@ -66,7 +66,15 @@ void CustomAllReduce(DLTensor* send, int strategy, DLTensor* 
recv) {
   int64_t num_elements = TensorSize(send);
   nccl::CCLThreadLocalContext* ctx = nccl::CCLThreadLocalContext::Get();
 
-  if (!CanApplyCustomAllReduce(num_elements, send->dtype)) {
+  tensorrt_llm::AllReduceStrategyType strategy_ =
+      static_cast<tensorrt_llm::AllReduceStrategyType>(strategy);
+  if (strategy_ == tensorrt_llm::AllReduceStrategyType::AUTO) {
+    strategy_ = tensorrt_llm::SelectImplementation(
+        num_elements * ((send->dtype.bits * send->dtype.lanes + 7) / 8), 
ctx->worker->num_workers);
+  }
+
+  if (strategy_ == tensorrt_llm::AllReduceStrategyType::RING ||
+      !CanApplyCustomAllReduce(num_elements, send->dtype)) {
     // Dispatch to nccl AllReduce if the customized all-reduce cannot apply.
     deviceStream_t stream = ctx->GetDefaultStream();
     NCCL_CALL(ncclAllReduce(send->data, recv->data, num_elements,
@@ -92,8 +100,6 @@ void CustomAllReduce(DLTensor* send, int strategy, DLTensor* 
recv) {
     params.peer_barrier_ptrs_out[i] = 
reinterpret_cast<uint32_t*>(ipc_memory->barrier_out[i]);
   }
 
-  tensorrt_llm::AllReduceStrategyType strategy_ =
-      static_cast<tensorrt_llm::AllReduceStrategyType>(strategy);
   if (!CanApplyTwoShotAllReduce(num_elements, send->dtype, 
ctx->worker->num_workers)) {
     // Two-shot all-reduce does not support this case.
     // So we fallback to the one-shot strategy.
diff --git a/tests/python/disco/test_custom_allreduce.py 
b/tests/python/disco/test_custom_allreduce.py
index 47b5f9590a..4aed32c052 100644
--- a/tests/python/disco/test_custom_allreduce.py
+++ b/tests/python/disco/test_custom_allreduce.py
@@ -29,15 +29,19 @@ from tvm.runtime.disco import Session
 
 
 class AllReduceStrategyType(enum.IntEnum):
+    RING = 0
     ONESHOT = 1
     TWOSHOT = 2
+    AUTO = 3
 
 
 _shapes = [(2, 3), (3, 4), (128, 128)]
 
 _strategies = [
+    AllReduceStrategyType.RING,
     AllReduceStrategyType.ONESHOT,
     AllReduceStrategyType.TWOSHOT,
+    AllReduceStrategyType.AUTO,
 ]
 
 _ccl = [ccl for ccl in tvm.get_global_func("runtime.disco.compiled_ccl")() if 
ccl == "nccl"]

Reply via email to