| 1 | #include <ATen/cuda/Exceptions.h> |
| 2 | #include <c10/cuda/CUDAGuard.h> |
| 3 | #include <c10/cuda/CUDAStream.h> |
| 4 | #include <torch/all.h> |
| 5 | |
| 6 | #ifdef USE_ROCM |
| 7 | |
| 8 | #include "quickreduce/quick_reduce.h" |
| 9 | |
| 10 | quickreduce::fptr_t init_custom_qr(int64_t rank, int64_t world_size, |
| 11 | std::optional<int64_t> qr_max_size) { |
| 12 | if (world_size > 8) |
| 13 | throw std::invalid_argument("world size > 8 is not supported"); |
| 14 | if (world_size == 6) |
| 15 | throw std::invalid_argument("world size == 6 is not supported"); |
| 16 | if (world_size % 2 != 0) |
| 17 | throw std::invalid_argument("Odd num gpus is not supported for now"); |
| 18 | if (rank < 0 || rank >= world_size) |
| 19 | throw std::invalid_argument("invalid rank passed in"); |
| 20 | quickreduce::DeviceComms* fptr = new quickreduce::DeviceComms(); |
| 21 | fptr->init(world_size, rank, qr_max_size); |
| 22 | return (quickreduce::fptr_t)fptr; |
| 23 | } |
| 24 | |
| 25 | void qr_destroy(quickreduce::fptr_t _fa) { |
| 26 | if (_fa) { |
| 27 | auto fa = reinterpret_cast<quickreduce::DeviceComms*>(_fa); |
| 28 | fa->destroy(); |
| 29 | delete fa; |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | torch::Tensor qr_get_handle(quickreduce::fptr_t _fa) { |
| 34 | auto fa = reinterpret_cast<quickreduce::DeviceComms*>(_fa); |
| 35 | hipIpcMemHandle_t handle = fa->get_handle(); |
| 36 | auto options = |
| 37 | torch::TensorOptions().dtype(torch::kUInt8).device(torch::kCPU); |
| 38 | auto data_handle = |
| 39 | torch::empty({static_cast<int64_t>(sizeof(hipIpcMemHandle_t))}, options); |
| 40 | std::memcpy(data_handle.data_ptr(), &handle, sizeof(hipIpcMemHandle_t)); |
| 41 | return data_handle; |
| 42 | } |
| 43 | |
| 44 | void qr_open_handles(quickreduce::fptr_t _fa, |
| 45 | const std::vector<torch::Tensor>& handles) { |
| 46 | auto fa = reinterpret_cast<quickreduce::DeviceComms*>(_fa); |
| 47 | std::vector<hipIpcMemHandle_t> ipc_handles; |
| 48 | ipc_handles.reserve(handles.size()); |
| 49 | for (auto& handle : handles) { |
| 50 | // Ensure the tensor is on the same device as the current device. |
| 51 | hipIpcMemHandle_t ipc_handle; |
| 52 | std::memcpy(&ipc_handle, handle.data_ptr(), sizeof(hipIpcMemHandle_t)); |
| 53 | ipc_handles.push_back(ipc_handle); |
| 54 | } |
| 55 | fa->open_ipc_handles(ipc_handles); |
| 56 | } |
| 57 | |
| 58 | void qr_all_reduce(quickreduce::fptr_t _fa, torch::Tensor& inp, |
| 59 | torch::Tensor& out, int64_t quant_level, bool cast_bf2half) { |
| 60 | auto fa = reinterpret_cast<quickreduce::DeviceComms*>(_fa); |
| 61 | const at::cuda::OptionalCUDAGuard device_guard(device_of(inp)); |
| 62 | auto stream = at::cuda::getCurrentHIPStreamMasqueradingAsCUDA(); |
| 63 | |
| 64 | TORCH_CHECK_EQ(inp.scalar_type(), out.scalar_type()); |
| 65 | TORCH_CHECK_EQ(inp.numel(), out.numel()); |
| 66 | TORCH_CHECK_LE(out.numel(), fa->kMaxProblemSize); |
| 67 | if (out.scalar_type() == at::ScalarType::Half) { |
| 68 | fa->allreduce<half, false>(reinterpret_cast<half*>(inp.data_ptr()), |
| 69 | reinterpret_cast<half*>(out.data_ptr()), |
| 70 | out.numel(), quant_level, stream); |
| 71 | } else if (out.scalar_type() == at::ScalarType::BFloat16) { |
| 72 | if (cast_bf2half) { |
| 73 | fa->allreduce<half, true>(reinterpret_cast<half*>(inp.data_ptr()), |
| 74 | reinterpret_cast<half*>(out.data_ptr()), |
| 75 | out.numel(), quant_level, stream); |
| 76 | } else { |
| 77 | fa->allreduce<quickreduce::nv_bfloat16, false>( |
| 78 | reinterpret_cast<quickreduce::nv_bfloat16*>(inp.data_ptr()), |
| 79 | reinterpret_cast<quickreduce::nv_bfloat16*>(out.data_ptr()), |
| 80 | out.numel(), quant_level, stream); |
| 81 | } |
| 82 | } else { |
| 83 | throw std::runtime_error( |
| 84 | "quick allreduce only supports float16 and bfloat16"); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | int64_t qr_max_size() { |
| 89 | // The default is 2GB (2,147,483,648 bytes) |
| 90 | return static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1; |
| 91 | } |
| 92 | |
| 93 | #define INSTANTIATE_FOR_WORLDSIZE(T, Codec, cast_bf2half) \ |
| 94 | template struct quickreduce::AllReduceTwoshot<T, Codec<T, 2>, \ |
| 95 | cast_bf2half>; \ |
| 96 | template struct quickreduce::AllReduceTwoshot<T, Codec<T, 4>, \ |
| 97 | cast_bf2half>; \ |
| 98 | template struct quickreduce::AllReduceTwoshot<T, Codec<T, 8>, cast_bf2half>; |
| 99 | |
| 100 | // INT3 (CodecQ3) is restricted to TP2 only, so we only instantiate the |
| 101 | // world_size == 2 kernel for it. |
| 102 | #define INSTANTIATE_FOR_WORLDSIZE_TP2_ONLY(T, Codec, cast_bf2half) \ |
| 103 | template struct quickreduce::AllReduceTwoshot<T, Codec<T, 2>, cast_bf2half>; |
| 104 | |
| 105 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecFP, false) |
| 106 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecQ4, false) |
| 107 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecQ6, false) |
| 108 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecQ8, false) |
| 109 | INSTANTIATE_FOR_WORLDSIZE_TP2_ONLY(quickreduce::nv_bfloat16, |
| 110 | quickreduce::CodecQ3, false) |
| 111 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecFP, true) |
| 112 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecQ4, true) |
| 113 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecQ6, true) |
| 114 | INSTANTIATE_FOR_WORLDSIZE(quickreduce::nv_bfloat16, quickreduce::CodecQ8, true) |
| 115 | INSTANTIATE_FOR_WORLDSIZE_TP2_ONLY(quickreduce::nv_bfloat16, |
| 116 | quickreduce::CodecQ3, true) |
| 117 | |
| 118 | INSTANTIATE_FOR_WORLDSIZE(half, quickreduce::CodecFP, false) |
| 119 | INSTANTIATE_FOR_WORLDSIZE(half, quickreduce::CodecQ4, false) |
| 120 | INSTANTIATE_FOR_WORLDSIZE(half, quickreduce::CodecQ6, false) |
| 121 | INSTANTIATE_FOR_WORLDSIZE(half, quickreduce::CodecQ8, false) |
| 122 | INSTANTIATE_FOR_WORLDSIZE_TP2_ONLY(half, quickreduce::CodecQ3, false) |
| 123 | |
| 124 | #endif // USE_ROCM |