59 lines
2.1 KiB
Python
59 lines
2.1 KiB
Python
# Some code borrowed from the awesome work: https://github.com/zhuzilin/ring-flash-attention
|
|
# Copyright (c) ModelScope Contributors. All rights reserved.
|
|
import torch
|
|
import torch.distributed as dist
|
|
from typing import Optional, Tuple
|
|
|
|
|
|
class RingComm:
|
|
|
|
def __init__(self, process_group: dist.ProcessGroup):
|
|
self._process_group = process_group
|
|
self._ops = []
|
|
self.rank = dist.get_rank(self._process_group)
|
|
self.world_size = dist.get_world_size(self._process_group)
|
|
self._reqs = None
|
|
|
|
self.send_rank = (self.rank + 1) % self.world_size
|
|
self.recv_rank = (self.rank - 1) % self.world_size
|
|
|
|
if process_group is not None:
|
|
self.send_rank = dist.get_global_rank(self._process_group, self.send_rank)
|
|
self.recv_rank = dist.get_global_rank(self._process_group, self.recv_rank)
|
|
|
|
def send_recv(self, to_send: torch.Tensor, recv_tensor: Optional[torch.Tensor] = None) -> torch.Tensor:
|
|
if recv_tensor is None:
|
|
res = torch.empty_like(to_send)
|
|
else:
|
|
res = recv_tensor
|
|
|
|
send_op = dist.P2POp(dist.isend, to_send, self.send_rank, group=self._process_group)
|
|
recv_op = dist.P2POp(dist.irecv, res, self.recv_rank, group=self._process_group)
|
|
self._ops.append(send_op)
|
|
self._ops.append(recv_op)
|
|
return res
|
|
|
|
def commit(self):
|
|
if self._reqs is not None:
|
|
raise RuntimeError('commit called twice')
|
|
self._reqs = dist.batch_isend_irecv(self._ops)
|
|
|
|
def wait(self):
|
|
if self._reqs is None:
|
|
raise RuntimeError('wait called before commit')
|
|
for req in self._reqs:
|
|
req.wait()
|
|
self._reqs = None
|
|
self._ops = []
|
|
|
|
def send_recv_kv(
|
|
self,
|
|
k: torch.Tensor,
|
|
v: torch.Tensor,
|
|
k_buffer: Optional[torch.Tensor] = None,
|
|
v_buffer: Optional[torch.Tensor] = None,
|
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
next_k, next_v = self.send_recv(k, k_buffer), self.send_recv(v, v_buffer)
|
|
self.commit()
|
|
return next_k, next_v
|