|
import os |
|
import socket |
|
import subprocess |
|
from datetime import timedelta |
|
|
|
import deepspeed |
|
import torch |
|
import torch.multiprocessing as mp |
|
from torch import distributed as dist |
|
|
|
timeout = timedelta(minutes=60) |
|
|
|
|
|
def _find_free_port(): |
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
|
sock.bind(('', 0)) |
|
port = sock.getsockname()[1] |
|
sock.close() |
|
|
|
return port |
|
|
|
|
|
def _is_free_port(port): |
|
ips = socket.gethostbyname_ex(socket.gethostname())[-1] |
|
ips.append('localhost') |
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: |
|
return all(s.connect_ex((ip, port)) != 0 for ip in ips) |
|
|
|
|
|
def init_dist(launcher, backend='nccl', **kwargs): |
|
if mp.get_start_method(allow_none=True) is None: |
|
mp.set_start_method('spawn') |
|
if launcher == 'pytorch': |
|
_init_dist_pytorch(backend, **kwargs) |
|
elif launcher == 'mpi': |
|
_init_dist_mpi(backend, **kwargs) |
|
elif launcher == 'slurm': |
|
_init_dist_slurm(backend, **kwargs) |
|
else: |
|
raise ValueError(f'Invalid launcher type: {launcher}') |
|
|
|
|
|
def _init_dist_pytorch(backend, **kwargs): |
|
|
|
rank = int(os.environ['RANK']) |
|
num_gpus = torch.cuda.device_count() |
|
torch.cuda.set_device(rank % num_gpus) |
|
|
|
deepspeed.init_distributed(dist_backend=backend) |
|
|
|
|
|
def _init_dist_mpi(backend, **kwargs): |
|
local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK']) |
|
torch.cuda.set_device(local_rank) |
|
if 'MASTER_PORT' not in os.environ: |
|
|
|
os.environ['MASTER_PORT'] = '29500' |
|
if 'MASTER_ADDR' not in os.environ: |
|
raise KeyError('The environment variable MASTER_ADDR is not set') |
|
os.environ['WORLD_SIZE'] = os.environ['OMPI_COMM_WORLD_SIZE'] |
|
os.environ['RANK'] = os.environ['OMPI_COMM_WORLD_RANK'] |
|
dist.init_process_group(backend=backend, **kwargs) |
|
|
|
|
|
def _init_dist_slurm(backend, port=None): |
|
"""Initialize slurm distributed training environment. |
|
|
|
If argument ``port`` is not specified, then the master port will be system |
|
environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system |
|
environment variable, then a default port ``29500`` will be used. |
|
|
|
Args: |
|
backend (str): Backend of torch.distributed. |
|
port (int, optional): Master port. Defaults to None. |
|
""" |
|
proc_id = int(os.environ['SLURM_PROCID']) |
|
ntasks = int(os.environ['SLURM_NTASKS']) |
|
node_list = os.environ['SLURM_NODELIST'] |
|
num_gpus = torch.cuda.device_count() |
|
torch.cuda.set_device(proc_id % num_gpus) |
|
addr = subprocess.getoutput( |
|
f'scontrol show hostname {node_list} | head -n1') |
|
|
|
if port is not None: |
|
os.environ['MASTER_PORT'] = str(port) |
|
elif 'MASTER_PORT' in os.environ: |
|
pass |
|
else: |
|
|
|
|
|
if _is_free_port(29500): |
|
os.environ['MASTER_PORT'] = '29500' |
|
else: |
|
os.environ['MASTER_PORT'] = str(_find_free_port()) |
|
|
|
if 'MASTER_ADDR' not in os.environ: |
|
os.environ['MASTER_ADDR'] = addr |
|
os.environ['WORLD_SIZE'] = str(ntasks) |
|
os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) |
|
os.environ['RANK'] = str(proc_id) |
|
|
|
deepspeed.init_distributed(dist_backend=backend) |
|
|