main
py 156 lines 5.31 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import torch
5 import torch.distributed as dist
6 from torch.distributed import ProcessGroup
7
8 import vllm.envs as envs
9 from vllm.distributed.device_communicators.all_reduce_utils import (
10 SYMM_MEM_ALL_REDUCE_MAX_SIZES,
11 )
12 from vllm.logger import init_logger
13 from vllm.platforms import current_platform
14
15 try:
16 import torch.distributed._symmetric_memory as torch_symm_mem
17
18 symm_mem_available = True
19 except ImportError:
20 symm_mem_available = False
21
22 logger = init_logger(__name__)
23
24
25 class SymmMemCommunicator:
26 _WORLD_SIZES_MULTIMEM = {
27 "9.0": [4, 6, 8],
28 "10.0": [6, 8],
29 "10.3": [6, 8],
30 "10.7": [6, 8], # sm_107 (Rubin): reuse 10.3 thresholds
31 }
32
33 def __init__(
34 self,
35 group: ProcessGroup,
36 device: int | str | torch.device,
37 # add options for testing
38 force_multimem: bool | None = None,
39 max_size_override: int | None = None,
40 ):
41 self.disabled = True
42
43 if not symm_mem_available:
44 return
45
46 if not current_platform.is_cuda():
47 logger.warning("SymmMemCommunicator: symmetric memory is not available.")
48 return
49 if isinstance(device, int):
50 device = torch.device(f"cuda:{device}")
51 elif isinstance(device, str):
52 device = torch.device(device)
53 torch.accelerator.set_device_index(device)
54 self.dtype = torch.bfloat16
55 self.device = device
56 self.group = group
57 self.world_size = dist.get_world_size(self.group)
58 capability = current_platform.get_device_capability()
59 if capability is None:
60 logger.warning(
61 "SymmMemCommunicator: device capability is unknown, "
62 "communicator is not available."
63 )
64 return
65 self.device_capability = capability.as_version_str()
66 if self.device_capability not in SYMM_MEM_ALL_REDUCE_MAX_SIZES:
67 logger.warning(
68 "SymmMemCommunicator: Device capability %s not supported, "
69 "communicator is not available.",
70 self.device_capability,
71 )
72 return
73 if self.world_size not in SYMM_MEM_ALL_REDUCE_MAX_SIZES[self.device_capability]:
74 logger.warning(
75 "SymmMemCommunicator: World size %d not supported, "
76 "communicator is not available.",
77 self.world_size,
78 )
79 return
80 # Use override max_size if provided, otherwise use default
81 if max_size_override is not None:
82 self.max_size = max_size_override
83 logger.info(
84 "SymmMemCommunicator: Using override max_size: %s bytes",
85 self.max_size,
86 )
87 else:
88 self.max_size = SYMM_MEM_ALL_REDUCE_MAX_SIZES[self.device_capability][
89 self.world_size
90 ]
91 try:
92 self.buffer = torch_symm_mem.empty(
93 self.max_size // self.dtype.itemsize,
94 device=self.device,
95 dtype=self.dtype,
96 )
97 handle = torch_symm_mem.rendezvous(self.buffer, self.group.group_name)
98 except RuntimeError as e:
99 logger.warning_once(
100 "SymmMemCommunicator: symmetric memory initialization failed: %s "
101 "Communicator is not available. To suppress this warning set "
102 "VLLM_ALLREDUCE_USE_SYMM_MEM=0",
103 str(e),
104 )
105 return
106 if handle.multicast_ptr == 0:
107 logger.warning(
108 "SymmMemCommunicator: symmetric memory "
109 "multicast operations are not supported."
110 )
111 return
112 self.force_multimem = force_multimem
113 self.disabled = False
114 if envs.VLLM_BATCH_INVARIANT:
115 self.disabled = True
116
117 def should_use_symm_mem(self, inp: torch.Tensor):
118 if self.disabled:
119 return False
120 if inp.dtype != self.dtype:
121 return False
122 inp_size = inp.numel() * inp.element_size()
123 if inp_size % 4 != 0:
124 return False
125 return inp_size <= self.max_size
126
127 def all_reduce(
128 self, inp: torch.Tensor, *, out: torch.Tensor | None = None
129 ) -> torch.Tensor | None:
130 if not self.should_use_symm_mem(inp):
131 return None
132 if out is None:
133 out = torch.empty_like(inp)
134 self.buffer[: inp.numel()].copy_(inp.view(-1))
135
136 # Determine which algorithm to use
137 use_multimem = False
138 if self.force_multimem is not None:
139 # Test override: use forced setting
140 use_multimem = self.force_multimem
141 else:
142 # Normal logic: use multimem for supported world sizes
143 use_multimem = (
144 self.world_size in self._WORLD_SIZES_MULTIMEM[self.device_capability]
145 )
146
147 if use_multimem:
148 torch.ops.symm_mem.multimem_all_reduce_(
149 self.buffer[: inp.numel()], "sum", self.group.group_name
150 )
151 else:
152 torch.ops.symm_mem.two_shot_all_reduce_(
153 self.buffer[: inp.numel()], "sum", self.group.group_name
154 )
155 out.copy_(self.buffer[: inp.numel()].view(out.shape))
156 return out