forked from Karylab-cklius/vllm
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d32045b96 | ||
|
|
41f6a7664a | ||
|
|
d35dfbb2e3 | ||
|
|
66eb0286ed | ||
|
|
7b879d0a98 | ||
|
|
4640e0d269 | ||
|
|
e3b4fdaf5d |
@@ -33,7 +33,6 @@ from vllm.distributed.device_communicators.custom_all_reduce import CustomAllred
|
||||
from vllm.distributed.device_communicators.flashinfer_all_reduce import (
|
||||
FlashInferAllReduce,
|
||||
)
|
||||
from vllm.distributed.device_communicators.push_all_reduce import PushAllReduce
|
||||
from vllm.distributed.device_communicators.pynccl import (
|
||||
PyNcclCommunicator,
|
||||
register_nccl_symmetric_ops,
|
||||
@@ -81,7 +80,6 @@ class CommunicatorBenchmark:
|
||||
|
||||
# Initialize communicators
|
||||
self.custom_allreduce = None
|
||||
self.push_ar_comm = None
|
||||
self.pynccl_comm = None
|
||||
self.symm_mem_comm = None
|
||||
self.symm_mem_comm_multimem = None
|
||||
@@ -108,23 +106,6 @@ class CommunicatorBenchmark:
|
||||
)
|
||||
self.custom_allreduce = None
|
||||
|
||||
try:
|
||||
self.push_ar_comm = PushAllReduce(
|
||||
group=self.cpu_group,
|
||||
device=self.device,
|
||||
max_size=self.max_size_override,
|
||||
)
|
||||
if not self.push_ar_comm.disabled:
|
||||
logger.info("Rank %s: PushAllReduce initialized", self.rank)
|
||||
else:
|
||||
logger.info("Rank %s: PushAllReduce disabled", self.rank)
|
||||
self.push_ar_comm = None
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Rank %s: Failed to initialize PushAllReduce: %s", self.rank, e
|
||||
)
|
||||
self.push_ar_comm = None
|
||||
|
||||
try:
|
||||
self.pynccl_comm = PyNcclCommunicator(
|
||||
group=self.cpu_group, device=self.device
|
||||
@@ -235,19 +216,6 @@ class CommunicatorBenchmark:
|
||||
)
|
||||
)
|
||||
|
||||
if self.push_ar_comm is not None:
|
||||
comm = self.push_ar_comm
|
||||
communicators.append(
|
||||
(
|
||||
"push_ar",
|
||||
lambda t, c=comm: c.all_reduce(t),
|
||||
lambda t, c=comm: c.should_use(t),
|
||||
comm.capture(),
|
||||
{},
|
||||
None,
|
||||
)
|
||||
)
|
||||
|
||||
if self.pynccl_comm is not None:
|
||||
comm = self.pynccl_comm
|
||||
communicators.append(
|
||||
|
||||
+17
-11
@@ -14,11 +14,12 @@ using fptr_t = int64_t;
|
||||
using namespace vllm::push_ar;
|
||||
|
||||
// Initialize the manager; returns opaque pointer as int64_t
|
||||
fptr_t init_push_ar(int64_t rank, int64_t world_size, int64_t push_buffer_bytes,
|
||||
int64_t max_num_cta) {
|
||||
auto* mgr = new PushAllReduceManager(
|
||||
static_cast<int>(rank), static_cast<int>(world_size), push_buffer_bytes,
|
||||
static_cast<int>(max_num_cta));
|
||||
fptr_t init_push_ar(int64_t rank, int64_t world_size,
|
||||
int64_t push_buffer_bytes, int64_t max_num_cta) {
|
||||
auto* mgr = new PushAllReduceManager(static_cast<int>(rank),
|
||||
static_cast<int>(world_size),
|
||||
push_buffer_bytes,
|
||||
static_cast<int>(max_num_cta));
|
||||
return reinterpret_cast<fptr_t>(mgr);
|
||||
}
|
||||
|
||||
@@ -26,7 +27,8 @@ fptr_t init_push_ar(int64_t rank, int64_t world_size, int64_t push_buffer_bytes,
|
||||
torch::Tensor get_push_ar_ipc_handle(fptr_t _mgr) {
|
||||
auto* mgr = reinterpret_cast<PushAllReduceManager*>(_mgr);
|
||||
cudaIpcMemHandle_t handle = mgr->get_ipc_handle();
|
||||
auto t = torch::from_blob(&handle, {static_cast<int64_t>(sizeof(handle))},
|
||||
auto t = torch::from_blob(&handle,
|
||||
{static_cast<int64_t>(sizeof(handle))},
|
||||
torch::kUInt8)
|
||||
.clone();
|
||||
return t;
|
||||
@@ -38,7 +40,8 @@ void post_init_push_ar(fptr_t _mgr, torch::Tensor all_handles) {
|
||||
int world_size = all_handles.size(0);
|
||||
std::vector<cudaIpcMemHandle_t> handles(world_size);
|
||||
for (int i = 0; i < world_size; i++) {
|
||||
memcpy(&handles[i], all_handles[i].data_ptr(), sizeof(cudaIpcMemHandle_t));
|
||||
memcpy(&handles[i], all_handles[i].data_ptr(),
|
||||
sizeof(cudaIpcMemHandle_t));
|
||||
}
|
||||
mgr->post_init(handles);
|
||||
}
|
||||
@@ -51,7 +54,8 @@ static bool _is_weak_contiguous(const torch::Tensor& t) {
|
||||
}
|
||||
|
||||
// Perform allreduce
|
||||
void push_ar_all_reduce(fptr_t _mgr, torch::Tensor& inp, torch::Tensor& out) {
|
||||
void push_ar_all_reduce(fptr_t _mgr, torch::Tensor& inp,
|
||||
torch::Tensor& out) {
|
||||
auto* mgr = reinterpret_cast<PushAllReduceManager*>(_mgr);
|
||||
const at::cuda::OptionalCUDAGuard device_guard(device_of(inp));
|
||||
auto stream = c10::cuda::getCurrentCUDAStream().stream();
|
||||
@@ -73,13 +77,15 @@ void push_ar_all_reduce(fptr_t _mgr, torch::Tensor& inp, torch::Tensor& out) {
|
||||
out.numel());
|
||||
break;
|
||||
case at::ScalarType::Float:
|
||||
mgr->allreduce<float>(stream, reinterpret_cast<float*>(inp.data_ptr()),
|
||||
mgr->allreduce<float>(stream,
|
||||
reinterpret_cast<float*>(inp.data_ptr()),
|
||||
reinterpret_cast<float*>(out.data_ptr()),
|
||||
out.numel());
|
||||
break;
|
||||
default:
|
||||
TORCH_CHECK(false,
|
||||
"push allreduce: unsupported dtype (need bf16/fp16/fp32)");
|
||||
TORCH_CHECK(
|
||||
false,
|
||||
"push allreduce: unsupported dtype (need bf16/fp16/fp32)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-16
@@ -21,14 +21,15 @@
|
||||
namespace vllm {
|
||||
namespace push_ar {
|
||||
|
||||
#define PUSH_AR_CUDACHECK(cmd) \
|
||||
do { \
|
||||
cudaError_t e = cmd; \
|
||||
if (e != cudaSuccess) { \
|
||||
throw std::runtime_error(std::string("push_all_reduce CUDA error at ") + \
|
||||
__FILE__ + ":" + std::to_string(__LINE__) + \
|
||||
" '" + cudaGetErrorString(e) + "'"); \
|
||||
} \
|
||||
#define PUSH_AR_CUDACHECK(cmd) \
|
||||
do { \
|
||||
cudaError_t e = cmd; \
|
||||
if (e != cudaSuccess) { \
|
||||
throw std::runtime_error( \
|
||||
std::string("push_all_reduce CUDA error at ") + __FILE__ + \
|
||||
":" + std::to_string(__LINE__) + " '" + \
|
||||
cudaGetErrorString(e) + "'"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
class PushAllReduceManager {
|
||||
@@ -88,8 +89,8 @@ class PushAllReduceManager {
|
||||
peer_storage_[i] = storage_;
|
||||
} else {
|
||||
PUSH_AR_CUDACHECK(cudaIpcOpenMemHandle(&peer_storage_[i],
|
||||
peer_handles[i],
|
||||
cudaIpcMemLazyEnablePeerAccess));
|
||||
peer_handles[i],
|
||||
cudaIpcMemLazyEnablePeerAccess));
|
||||
}
|
||||
}
|
||||
// Create PushController pointing to local signal region
|
||||
@@ -107,12 +108,13 @@ class PushAllReduceManager {
|
||||
const int num_threads = select_num_threads<T>(num_items);
|
||||
|
||||
// Verify input fits in push buffer (runtime check, not compiled out)
|
||||
const int64_t input_bytes = static_cast<int64_t>(sizeof(T)) * num_elements;
|
||||
const int64_t input_bytes =
|
||||
static_cast<int64_t>(sizeof(T)) * num_elements;
|
||||
if (input_bytes > push_buffer_bytes_) {
|
||||
throw std::runtime_error("push_all_reduce: input (" +
|
||||
std::to_string(input_bytes) +
|
||||
" bytes) exceeds push buffer capacity (" +
|
||||
std::to_string(push_buffer_bytes_) + " bytes)");
|
||||
throw std::runtime_error(
|
||||
"push_all_reduce: input (" + std::to_string(input_bytes) +
|
||||
" bytes) exceeds push buffer capacity (" +
|
||||
std::to_string(push_buffer_bytes_) + " bytes)");
|
||||
}
|
||||
|
||||
// Build kernel params
|
||||
@@ -223,7 +225,9 @@ class PushAllReduceManager {
|
||||
return static_cast<char*>(base) + push_signal_bytes();
|
||||
}
|
||||
|
||||
static int64_t align128(int64_t size) { return ((size + 127) / 128) * 128; }
|
||||
static int64_t align128(int64_t size) {
|
||||
return ((size + 127) / 128) * 128;
|
||||
}
|
||||
|
||||
// Members
|
||||
int rank_;
|
||||
|
||||
@@ -235,8 +235,9 @@ struct alignas(sizeof(T) * N) AlignedStorage {
|
||||
template <typename T, std::size_t N>
|
||||
struct AlignedVector {
|
||||
private:
|
||||
static_assert((N > 0 && (N & (N - 1)) == 0) && sizeof(T) * N <= kMaxVecBytes,
|
||||
"CUDA vector size exceeds arch limit (max 16 bytes)");
|
||||
static_assert(
|
||||
(N > 0 && (N & (N - 1)) == 0) && sizeof(T) * N <= kMaxVecBytes,
|
||||
"CUDA vector size exceeds arch limit (max 16 bytes)");
|
||||
using element_t = typename detail::sized_int<T>;
|
||||
using storage_t = AlignedStorage<element_t, N>;
|
||||
|
||||
@@ -393,7 +394,8 @@ __device__ __forceinline__ void st_global_volatile_16B(const T& x, void* addr,
|
||||
static_assert(alignof(T) == 16 && sizeof(T) == 16);
|
||||
const uint4 val = *reinterpret_cast<const uint4*>(&x);
|
||||
addr = ptr_typed_offset<T>(addr, offset);
|
||||
asm volatile("st.volatile.global.v4.b32 [%4], {%0, %1, %2, %3};" ::"r"(val.x),
|
||||
asm volatile("st.volatile.global.v4.b32 [%4], {%0, %1, %2, %3};" ::"r"(
|
||||
val.x),
|
||||
"r"(val.y), "r"(val.z), "r"(val.w), "l"(addr));
|
||||
}
|
||||
|
||||
|
||||
@@ -105,8 +105,8 @@ TORCH_LIBRARY_EXPAND(CONCAT(TORCH_EXTENSION_NAME, _cuda_utils), cuda_utils) {
|
||||
#endif
|
||||
|
||||
// Push-based allreduce (ported from SGLang)
|
||||
fptr_t init_push_ar(int64_t rank, int64_t world_size, int64_t push_buffer_bytes,
|
||||
int64_t max_num_cta);
|
||||
fptr_t init_push_ar(int64_t rank, int64_t world_size,
|
||||
int64_t push_buffer_bytes, int64_t max_num_cta);
|
||||
torch::Tensor get_push_ar_ipc_handle(fptr_t _mgr);
|
||||
void post_init_push_ar(fptr_t _mgr, torch::Tensor all_handles);
|
||||
void push_ar_all_reduce(fptr_t _mgr, torch::Tensor& inp, torch::Tensor& out);
|
||||
@@ -119,7 +119,8 @@ TORCH_LIBRARY_EXPAND(CONCAT(TORCH_EXTENSION_NAME, _push_ar), push_ar) {
|
||||
|
||||
push_ar.def("post_init_push_ar", &post_init_push_ar);
|
||||
|
||||
push_ar.def("push_ar_all_reduce(int mgr, Tensor inp, Tensor! out) -> ()");
|
||||
push_ar.def(
|
||||
"push_ar_all_reduce(int mgr, Tensor inp, Tensor! out) -> ()");
|
||||
push_ar.impl("push_ar_all_reduce", torch::kCUDA, &push_ar_all_reduce);
|
||||
|
||||
push_ar.def("dispose_push_ar", &dispose_push_ar);
|
||||
|
||||
@@ -39,9 +39,11 @@ def init_groups(rank: int, world_size: int, port: int):
|
||||
os.environ["MASTER_ADDR"] = "localhost"
|
||||
os.environ["MASTER_PORT"] = str(port)
|
||||
|
||||
torch.accelerator.set_device_index(rank)
|
||||
torch.cuda.set_device(rank)
|
||||
|
||||
dist.init_process_group(backend="gloo", rank=rank, world_size=world_size)
|
||||
dist.init_process_group(
|
||||
backend="gloo", rank=rank, world_size=world_size
|
||||
)
|
||||
_cpu_group = dist.group.WORLD
|
||||
|
||||
# Create a separate NCCL group for reference allreduce
|
||||
|
||||
@@ -38,7 +38,7 @@ def _init_groups(rank: int, world_size: int, port: int):
|
||||
"""Initialize gloo (CPU) and nccl process groups."""
|
||||
os.environ["MASTER_ADDR"] = "localhost"
|
||||
os.environ["MASTER_PORT"] = str(port)
|
||||
torch.accelerator.set_device_index(rank)
|
||||
torch.cuda.set_device(rank)
|
||||
dist.init_process_group(backend="gloo", rank=rank, world_size=world_size)
|
||||
return dist.group.WORLD, dist.new_group(backend="nccl")
|
||||
|
||||
@@ -94,7 +94,7 @@ def _push_ar_init_worker(rank, world_size, port):
|
||||
|
||||
@pytest.mark.parametrize("world_size", [2])
|
||||
def test_push_ar_initialization(world_size):
|
||||
if torch.accelerator.device_count() < world_size:
|
||||
if torch.cuda.device_count() < world_size:
|
||||
pytest.skip(f"Need {world_size} GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_init_worker,
|
||||
@@ -159,7 +159,7 @@ def _push_ar_should_use_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_should_use():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_should_use_worker,
|
||||
@@ -204,7 +204,7 @@ def _push_ar_correctness_int_worker(rank, world_size, port):
|
||||
|
||||
@pytest.mark.parametrize("world_size", [2])
|
||||
def test_push_ar_correctness_integer(world_size):
|
||||
if torch.accelerator.device_count() < world_size:
|
||||
if torch.cuda.device_count() < world_size:
|
||||
pytest.skip(f"Need {world_size} GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_correctness_int_worker,
|
||||
@@ -253,7 +253,7 @@ def _push_ar_correctness_float_worker(rank, world_size, port):
|
||||
|
||||
@pytest.mark.parametrize("world_size", [2])
|
||||
def test_push_ar_correctness_float(world_size):
|
||||
if torch.accelerator.device_count() < world_size:
|
||||
if torch.cuda.device_count() < world_size:
|
||||
pytest.skip(f"Need {world_size} GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_correctness_float_worker,
|
||||
@@ -302,7 +302,9 @@ def _push_ar_zero_handling_worker(rank, world_size, port):
|
||||
assert torch.all(out_pz == 0.0), "Positive zero handling failed"
|
||||
|
||||
# Case 4: Negative zeros
|
||||
inp_nz = torch.tensor([-0.0] * 1024, dtype=torch.bfloat16, device=device)
|
||||
inp_nz = torch.tensor(
|
||||
[-0.0] * 1024, dtype=torch.bfloat16, device=device
|
||||
)
|
||||
out_nz = push_ar.all_reduce(inp_nz)
|
||||
assert torch.all(out_nz == 0.0)
|
||||
|
||||
@@ -317,7 +319,7 @@ def _push_ar_zero_handling_worker(rank, world_size, port):
|
||||
|
||||
@pytest.mark.timeout(120)
|
||||
def test_push_ar_zero_handling():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_zero_handling_worker,
|
||||
@@ -343,20 +345,24 @@ def _push_ar_epoch_worker(rank, world_size, port):
|
||||
NUM_ITERATIONS = 1000
|
||||
|
||||
for i in range(NUM_ITERATIONS):
|
||||
inp = torch.randint(0, 16, (7168,), dtype=torch.bfloat16, device=device)
|
||||
inp = torch.randint(
|
||||
0, 16, (7168,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
out_push = push_ar.all_reduce(inp)
|
||||
|
||||
out_nccl = inp.clone()
|
||||
dist.all_reduce(out_nccl, group=nccl_group)
|
||||
|
||||
assert torch.all(out_push == out_nccl), f"Epoch mismatch at iteration {i}"
|
||||
assert torch.all(out_push == out_nccl), (
|
||||
f"Epoch mismatch at iteration {i}"
|
||||
)
|
||||
|
||||
push_ar.close()
|
||||
_teardown()
|
||||
|
||||
|
||||
def test_push_ar_epoch_alternation():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_epoch_worker,
|
||||
@@ -397,18 +403,22 @@ def _push_ar_thread_count_worker(rank, world_size, port):
|
||||
for size in test_sizes_tc:
|
||||
if size * 2 > push_ar.max_message_bytes:
|
||||
continue
|
||||
inp = torch.randint(0, 8, (size,), dtype=torch.bfloat16, device=device)
|
||||
inp = torch.randint(
|
||||
0, 8, (size,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
out = push_ar.all_reduce(inp)
|
||||
ref = inp.clone()
|
||||
dist.all_reduce(ref, group=nccl_group)
|
||||
assert torch.all(out == ref), f"Failed at size={size} (sm_count={sm_count})"
|
||||
assert torch.all(out == ref), (
|
||||
f"Failed at size={size} (sm_count={sm_count})"
|
||||
)
|
||||
|
||||
push_ar.close()
|
||||
_teardown()
|
||||
|
||||
|
||||
def test_push_ar_thread_count():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_thread_count_worker,
|
||||
@@ -436,7 +446,9 @@ def _push_ar_threshold_worker(rank, world_size, port):
|
||||
# Case 1: Exactly at threshold (BF16)
|
||||
max_elems = max_bytes // 2
|
||||
max_elems = (max_elems // 8) * 8 # align to 16 bytes
|
||||
inp_exact = torch.randint(0, 8, (max_elems,), dtype=torch.bfloat16, device=device)
|
||||
inp_exact = torch.randint(
|
||||
0, 8, (max_elems,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
assert push_ar.should_use(inp_exact) is True
|
||||
out_exact = push_ar.all_reduce(inp_exact)
|
||||
ref_exact = inp_exact.clone()
|
||||
@@ -464,7 +476,7 @@ def _push_ar_threshold_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_threshold():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_threshold_worker,
|
||||
@@ -487,7 +499,9 @@ def _push_ar_outofplace_worker(rank, world_size, port):
|
||||
|
||||
push_ar = PushAllReduce(group=cpu_group, device=device)
|
||||
|
||||
inp = torch.randint(1, 16, (7168,), dtype=torch.bfloat16, device=device)
|
||||
inp = torch.randint(
|
||||
1, 16, (7168,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
inp_original = inp.clone()
|
||||
|
||||
out = push_ar.all_reduce(inp)
|
||||
@@ -508,7 +522,7 @@ def _push_ar_outofplace_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_outofplace():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_outofplace_worker,
|
||||
@@ -548,7 +562,7 @@ def _push_ar_dtype_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_dtype():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_dtype_worker,
|
||||
@@ -576,19 +590,23 @@ def _push_ar_multilayer_worker(rank, world_size, port):
|
||||
|
||||
for step in range(NUM_STEPS):
|
||||
for layer in range(NUM_LAYERS):
|
||||
inp = torch.randint(0, 16, (7168,), dtype=torch.bfloat16, device=device)
|
||||
inp = torch.randint(
|
||||
0, 16, (7168,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
out = push_ar.all_reduce(inp)
|
||||
ref = inp.clone()
|
||||
dist.all_reduce(ref, group=nccl_group)
|
||||
if not torch.all(out == ref):
|
||||
raise RuntimeError(f"Mismatch at step={step}, layer={layer}")
|
||||
raise RuntimeError(
|
||||
f"Mismatch at step={step}, layer={layer}"
|
||||
)
|
||||
|
||||
push_ar.close()
|
||||
_teardown()
|
||||
|
||||
|
||||
def test_push_ar_multilayer():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_multilayer_worker,
|
||||
@@ -612,7 +630,9 @@ def _push_ar_lifecycle_worker(rank, world_size, port):
|
||||
push_ar = PushAllReduce(group=cpu_group, device=device)
|
||||
|
||||
# Normal allreduce works
|
||||
inp = torch.randint(0, 16, (1024,), dtype=torch.bfloat16, device=device)
|
||||
inp = torch.randint(
|
||||
0, 16, (1024,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
out = push_ar.all_reduce(inp)
|
||||
assert out is not None
|
||||
|
||||
@@ -630,7 +650,7 @@ def _push_ar_lifecycle_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_lifecycle():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_lifecycle_worker,
|
||||
@@ -676,7 +696,7 @@ def _push_ar_warmup_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_warmup():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_warmup_worker,
|
||||
@@ -720,14 +740,16 @@ def _push_ar_asymmetric_worker(rank, world_size, port):
|
||||
all_inputs = [torch.empty_like(inp) for _ in range(world_size)]
|
||||
dist.all_gather(all_inputs, inp, group=nccl_group)
|
||||
expected_sum = torch.stack(all_inputs).float().sum(dim=0).to(inp.dtype)
|
||||
torch.testing.assert_close(out_push, expected_sum, atol=5e-2, rtol=5e-2)
|
||||
torch.testing.assert_close(
|
||||
out_push, expected_sum, atol=5e-2, rtol=5e-2
|
||||
)
|
||||
|
||||
push_ar.close()
|
||||
_teardown()
|
||||
|
||||
|
||||
def test_push_ar_asymmetric():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_asymmetric_worker,
|
||||
@@ -760,7 +782,7 @@ def _push_ar_identical_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_identical_values():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_identical_worker,
|
||||
@@ -801,14 +823,16 @@ def _dispatch_priority_worker(rank, world_size, port):
|
||||
out_small = push_ar.all_reduce(small_inp)
|
||||
ref_small = small_inp.clone()
|
||||
dist.all_reduce(ref_small, group=nccl_group)
|
||||
torch.testing.assert_close(out_small, ref_small, atol=1e-2, rtol=1e-2)
|
||||
torch.testing.assert_close(
|
||||
out_small, ref_small, atol=1e-2, rtol=1e-2
|
||||
)
|
||||
|
||||
push_ar.close()
|
||||
_teardown()
|
||||
|
||||
|
||||
def test_dispatch_priority():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_dispatch_priority_worker,
|
||||
@@ -833,7 +857,9 @@ def _coexistence_worker(rank, world_size, port):
|
||||
|
||||
for _ in range(50):
|
||||
# Small message -> push allreduce
|
||||
small = torch.randint(0, 16, (7168,), dtype=torch.bfloat16, device=device)
|
||||
small = torch.randint(
|
||||
0, 16, (7168,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
out_small = push_ar.all_reduce(small)
|
||||
ref_small = small.clone()
|
||||
dist.all_reduce(ref_small, group=nccl_group)
|
||||
@@ -844,7 +870,7 @@ def _coexistence_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_coexistence():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_coexistence_worker,
|
||||
@@ -869,8 +895,12 @@ def _interleaved_ar_worker(rank, world_size, port):
|
||||
|
||||
for sz in [7168, 1024, 4096]:
|
||||
for dtype in [torch.bfloat16, torch.float16]:
|
||||
inp1 = torch.randint(1, 16, (sz,), dtype=dtype, device=device)
|
||||
inp2 = torch.randint(1, 16, (sz,), dtype=dtype, device=device)
|
||||
inp1 = torch.randint(
|
||||
1, 16, (sz,), dtype=dtype, device=device
|
||||
)
|
||||
inp2 = torch.randint(
|
||||
1, 16, (sz,), dtype=dtype, device=device
|
||||
)
|
||||
|
||||
out1 = push_ar.all_reduce(inp1)
|
||||
ref1 = inp1.clone()
|
||||
@@ -880,15 +910,19 @@ def _interleaved_ar_worker(rank, world_size, port):
|
||||
ref2 = inp2.clone()
|
||||
dist.all_reduce(ref2, group=nccl_group)
|
||||
|
||||
torch.testing.assert_close(out1, ref1, atol=1e-2, rtol=1e-2)
|
||||
torch.testing.assert_close(out2, ref2, atol=1e-2, rtol=1e-2)
|
||||
torch.testing.assert_close(
|
||||
out1, ref1, atol=1e-2, rtol=1e-2
|
||||
)
|
||||
torch.testing.assert_close(
|
||||
out2, ref2, atol=1e-2, rtol=1e-2
|
||||
)
|
||||
|
||||
push_ar.close()
|
||||
_teardown()
|
||||
|
||||
|
||||
def test_interleaved_ar():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_interleaved_ar_worker,
|
||||
@@ -915,13 +949,15 @@ def _graph_capture_worker(rank, world_size, port):
|
||||
sz = 7168
|
||||
|
||||
# Allocate graph input in graph memory pool
|
||||
graph_inp = torch.randint(1, 16, (sz,), dtype=torch.bfloat16, device=device)
|
||||
graph_inp = torch.randint(
|
||||
1, 16, (sz,), dtype=torch.bfloat16, device=device
|
||||
)
|
||||
|
||||
# Warmup
|
||||
with push_ar.capture():
|
||||
for _ in range(NUM_AR):
|
||||
push_ar.all_reduce(graph_inp)
|
||||
torch.accelerator.synchronize()
|
||||
torch.cuda.synchronize()
|
||||
|
||||
# Capture graph
|
||||
graph = torch.cuda.CUDAGraph()
|
||||
@@ -937,7 +973,7 @@ def _graph_capture_worker(rank, world_size, port):
|
||||
torch.randint(1, 16, (sz,), dtype=torch.bfloat16, device=device)
|
||||
)
|
||||
graph.replay()
|
||||
torch.accelerator.synchronize()
|
||||
torch.cuda.synchronize()
|
||||
|
||||
# Verify last output is correct
|
||||
ref = graph_inp.clone()
|
||||
@@ -946,11 +982,8 @@ def _graph_capture_worker(rank, world_size, port):
|
||||
# of graph_inp.
|
||||
dist.all_reduce(ref, group=nccl_group)
|
||||
torch.testing.assert_close(
|
||||
outs[-1],
|
||||
ref,
|
||||
atol=1e-2,
|
||||
rtol=1e-2,
|
||||
msg=f"Graph replay {replay_iter} failed",
|
||||
outs[-1], ref, atol=1e-2, rtol=1e-2,
|
||||
msg=f"Graph replay {replay_iter} failed"
|
||||
)
|
||||
|
||||
push_ar.close()
|
||||
@@ -958,7 +991,7 @@ def _graph_capture_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_graph_capture():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_graph_capture_worker,
|
||||
@@ -988,7 +1021,9 @@ def _push_ar_transformer_sim_worker(rank, world_size, port):
|
||||
for step in range(NUM_STEPS):
|
||||
for block in range(NUM_BLOCKS):
|
||||
# AR 1: attention wo_b output
|
||||
attn_out = torch.randn(1, hidden_size, dtype=torch.bfloat16, device=device)
|
||||
attn_out = torch.randn(
|
||||
1, hidden_size, dtype=torch.bfloat16, device=device
|
||||
)
|
||||
attn_reduced = push_ar.all_reduce(attn_out)
|
||||
attn_ref = attn_out.clone()
|
||||
dist.all_reduce(attn_ref, group=nccl_group)
|
||||
@@ -1001,7 +1036,9 @@ def _push_ar_transformer_sim_worker(rank, world_size, port):
|
||||
)
|
||||
|
||||
# AR 2: MoE output
|
||||
moe_out = torch.randn(1, hidden_size, dtype=torch.bfloat16, device=device)
|
||||
moe_out = torch.randn(
|
||||
1, hidden_size, dtype=torch.bfloat16, device=device
|
||||
)
|
||||
moe_reduced = push_ar.all_reduce(moe_out)
|
||||
moe_ref = moe_out.clone()
|
||||
dist.all_reduce(moe_ref, group=nccl_group)
|
||||
@@ -1018,7 +1055,7 @@ def _push_ar_transformer_sim_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_push_ar_transformer_sim():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_push_ar_transformer_sim_worker,
|
||||
@@ -1039,9 +1076,9 @@ def _feature_toggle_enabled_worker(rank, world_size, port):
|
||||
os.environ.pop("VLLM_DISABLE_PUSH_ALLREDUCE", None)
|
||||
|
||||
from vllm.distributed.device_communicators.push_all_reduce import (
|
||||
_DISABLE_ENV_VAR,
|
||||
_FEATURE_DESCRIPTION,
|
||||
PushAllReduce,
|
||||
_FEATURE_DESCRIPTION,
|
||||
_DISABLE_ENV_VAR,
|
||||
)
|
||||
|
||||
push_ar = PushAllReduce(group=cpu_group, device=device)
|
||||
@@ -1062,7 +1099,7 @@ def _feature_toggle_enabled_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_feature_toggle_enabled():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_feature_toggle_enabled_worker,
|
||||
@@ -1102,7 +1139,7 @@ def _feature_toggle_disabled_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_feature_toggle_disabled():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_feature_toggle_disabled_worker,
|
||||
@@ -1129,7 +1166,9 @@ def _feature_toggle_not_disabled_worker(rank, world_size, port):
|
||||
push_ar = PushAllReduce(group=cpu_group, device=device)
|
||||
|
||||
# Feature should still be enabled (only "1" disables)
|
||||
assert not push_ar.disabled, "PushAllReduce should be enabled when env var != '1'"
|
||||
assert not push_ar.disabled, (
|
||||
"PushAllReduce should be enabled when env var != '1'"
|
||||
)
|
||||
|
||||
# Clean up
|
||||
os.environ.pop("VLLM_DISABLE_PUSH_ALLREDUCE", None)
|
||||
@@ -1138,7 +1177,7 @@ def _feature_toggle_not_disabled_worker(rank, world_size, port):
|
||||
|
||||
|
||||
def test_feature_toggle_not_disabled():
|
||||
if torch.accelerator.device_count() < 2:
|
||||
if torch.cuda.device_count() < 2:
|
||||
pytest.skip("Need 2 GPUs")
|
||||
mp.spawn(
|
||||
_feature_toggle_not_disabled_worker,
|
||||
|
||||
+9
-3
@@ -3024,8 +3024,12 @@ def qr_max_size() -> int:
|
||||
|
||||
|
||||
# push allreduce (ported from SGLang)
|
||||
def init_push_ar(rank: int, world_size: int, buffer_bytes: int, max_cta: int) -> int:
|
||||
return torch.ops._C_push_ar.init_push_ar(rank, world_size, buffer_bytes, max_cta)
|
||||
def init_push_ar(
|
||||
rank: int, world_size: int, buffer_bytes: int, max_cta: int
|
||||
) -> int:
|
||||
return torch.ops._C_push_ar.init_push_ar(
|
||||
rank, world_size, buffer_bytes, max_cta
|
||||
)
|
||||
|
||||
|
||||
def get_push_ar_ipc_handle(mgr: int) -> torch.Tensor:
|
||||
@@ -3036,7 +3040,9 @@ def post_init_push_ar(mgr: int, handles: torch.Tensor) -> None:
|
||||
torch.ops._C_push_ar.post_init_push_ar(mgr, handles)
|
||||
|
||||
|
||||
def push_ar_all_reduce(mgr: int, inp: torch.Tensor, out: torch.Tensor) -> None:
|
||||
def push_ar_all_reduce(
|
||||
mgr: int, inp: torch.Tensor, out: torch.Tensor
|
||||
) -> None:
|
||||
torch.ops._C_push_ar.push_ar_all_reduce(mgr, inp, out)
|
||||
|
||||
|
||||
|
||||
@@ -113,11 +113,9 @@ class CudaCommunicator(DeviceCommunicatorBase):
|
||||
|
||||
# Initialize push-based allreduce (faster for small messages)
|
||||
# Only available on NVIDIA CUDA GPUs with NVLink
|
||||
if (
|
||||
current_platform.is_cuda()
|
||||
and self.ca_comm is not None
|
||||
and not self.ca_comm.disabled
|
||||
):
|
||||
if (current_platform.is_cuda()
|
||||
and self.ca_comm is not None
|
||||
and not self.ca_comm.disabled):
|
||||
try:
|
||||
from vllm.distributed.device_communicators.push_all_reduce import (
|
||||
PushAllReduce,
|
||||
@@ -226,7 +224,6 @@ class CudaCommunicator(DeviceCommunicatorBase):
|
||||
"NCCL_SYMM_MEM",
|
||||
"QUICK_REDUCE",
|
||||
"FLASHINFER",
|
||||
"PUSH_AR",
|
||||
"CUSTOM",
|
||||
"SYMM_MEM",
|
||||
"PYNCCL",
|
||||
@@ -260,8 +257,6 @@ class CudaCommunicator(DeviceCommunicatorBase):
|
||||
enabled_ar_backends.append("QUICK_REDUCE")
|
||||
if self.fi_ar_comm is not None and not self.fi_ar_comm.disabled:
|
||||
enabled_ar_backends.append("FLASHINFER")
|
||||
if self.push_ar_comm is not None:
|
||||
enabled_ar_backends.append("PUSH_AR")
|
||||
if self.ca_comm is not None and not self.ca_comm.disabled:
|
||||
enabled_ar_backends.append("CUSTOM")
|
||||
if self.symm_mem_comm is not None and not self.symm_mem_comm.disabled:
|
||||
@@ -308,7 +303,10 @@ class CudaCommunicator(DeviceCommunicatorBase):
|
||||
assert out is not None
|
||||
return out
|
||||
push_ar_comm = self.push_ar_comm
|
||||
if push_ar_comm is not None and push_ar_comm.should_use(input_):
|
||||
if (
|
||||
push_ar_comm is not None
|
||||
and push_ar_comm.should_use(input_)
|
||||
):
|
||||
out = push_ar_comm.all_reduce(input_)
|
||||
if out is not None:
|
||||
return out
|
||||
|
||||
@@ -16,6 +16,7 @@ Protocol:
|
||||
import logging
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from typing import Optional
|
||||
|
||||
import torch
|
||||
import torch.distributed as dist
|
||||
@@ -70,7 +71,7 @@ class PushAllReduce:
|
||||
self,
|
||||
group: dist.ProcessGroup,
|
||||
device: torch.device,
|
||||
max_size: int | None = None,
|
||||
max_size: Optional[int] = None,
|
||||
):
|
||||
self.group = group
|
||||
self.device = device
|
||||
@@ -110,7 +111,9 @@ class PushAllReduce:
|
||||
self.push_buffer_bytes = max_size
|
||||
else:
|
||||
arch_major = props.major
|
||||
threshold_map = _THRESHOLD_BY_ARCH.get(arch_major, PUSH_THRESHOLD_DEFAULT)
|
||||
threshold_map = _THRESHOLD_BY_ARCH.get(
|
||||
arch_major, PUSH_THRESHOLD_DEFAULT
|
||||
)
|
||||
self.push_buffer_bytes = threshold_map.get(
|
||||
self.world_size, DEFAULT_PUSH_BUFFER
|
||||
)
|
||||
@@ -128,7 +131,9 @@ class PushAllReduce:
|
||||
if env_override:
|
||||
val = int(env_override)
|
||||
if val == 0:
|
||||
logger.info("PushAllReduce disabled via VLLM_PUSH_AR_BUFFER_BYTES=0")
|
||||
logger.info(
|
||||
"PushAllReduce disabled via VLLM_PUSH_AR_BUFFER_BYTES=0"
|
||||
)
|
||||
self.disabled = True
|
||||
return
|
||||
# Round up to 128-byte alignment for kernel volatile stores
|
||||
@@ -149,7 +154,8 @@ class PushAllReduce:
|
||||
self._exchange_ipc_handles()
|
||||
|
||||
logger.info(
|
||||
"PushAllReduce initialized: rank=%d, ws=%d, buffer=%d KB, sm=%d",
|
||||
"PushAllReduce initialized: rank=%d, ws=%d, "
|
||||
"buffer=%d KB, sm=%d",
|
||||
self.rank,
|
||||
self.world_size,
|
||||
self.push_buffer_bytes // 1024,
|
||||
@@ -180,7 +186,9 @@ class PushAllReduce:
|
||||
local_handle = ops.get_push_ar_ipc_handle(self._ptr) # shape (64,)
|
||||
|
||||
# All-gather handles: each rank broadcasts its handle
|
||||
handle_list = [torch.empty_like(local_handle) for _ in range(self.world_size)]
|
||||
handle_list = [
|
||||
torch.empty_like(local_handle) for _ in range(self.world_size)
|
||||
]
|
||||
dist.all_gather(handle_list, local_handle, group=self.group)
|
||||
all_handles = torch.stack(handle_list) # shape (world_size, 64)
|
||||
|
||||
@@ -200,7 +208,7 @@ class PushAllReduce:
|
||||
return False
|
||||
return inp_size <= self.max_message_bytes
|
||||
|
||||
def all_reduce(self, input_: torch.Tensor) -> torch.Tensor | None:
|
||||
def all_reduce(self, input_: torch.Tensor) -> Optional[torch.Tensor]:
|
||||
"""Perform push-based allreduce. Returns new output tensor."""
|
||||
if self._IS_CAPTURING:
|
||||
if torch.cuda.is_current_stream_capturing():
|
||||
|
||||
@@ -590,7 +590,8 @@ class GroupCoordinator:
|
||||
maybe_ca_context = ca_comm.capture() # type: ignore
|
||||
|
||||
# Enter push allreduce capture context
|
||||
push_ar_comm = getattr(self.device_communicator, "push_ar_comm", None)
|
||||
push_ar_comm = getattr(
|
||||
self.device_communicator, 'push_ar_comm', None)
|
||||
if push_ar_comm is not None:
|
||||
maybe_push_ar_context = push_ar_comm.capture() # type: ignore
|
||||
|
||||
@@ -607,12 +608,7 @@ class GroupCoordinator:
|
||||
if curr_stream != stream:
|
||||
stream.wait_stream(curr_stream)
|
||||
|
||||
with (
|
||||
torch.cuda.stream(stream),
|
||||
maybe_ca_context,
|
||||
maybe_push_ar_context,
|
||||
maybe_aiter_context,
|
||||
):
|
||||
with torch.cuda.stream(stream), maybe_ca_context, maybe_push_ar_context, maybe_aiter_context:
|
||||
yield graph_capture_context
|
||||
|
||||
def all_reduce(self, input_: torch.Tensor) -> torch.Tensor:
|
||||
|
||||
Reference in New Issue
Block a user