CoolFace
Apppublic

facebook/StyleNeRF

sourceHugging Faceupdated 4y agoView on Hugging Face
34likes
distributed_utils.py214 linesDownload Raw Back to torch_utils
1# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved2 3import logging4import os5import pickle6import random7import socket8import struct9import subprocess10import warnings11import tempfile12import uuid13 14 15from datetime import date16from pathlib import Path17from collections import OrderedDict18from typing import Any, Dict, Mapping19 20import torch21import torch.distributed as dist22 23 24logger = logging.getLogger(__name__)25 26 27def is_master(args):28    return args.distributed_rank == 029 30 31def init_distributed_mode(rank, args):32    if "WORLD_SIZE" in os.environ:33        args.world_size = int(os.environ["WORLD_SIZE"])34    35    if args.launcher == 'spawn':  # single node with multiprocessing.spawn36        args.world_size = args.num_gpus37        args.rank = rank38        args.gpu = rank39    40    elif 'RANK' in os.environ:41        args.rank = int(os.environ["RANK"])42        args.gpu = int(os.environ['LOCAL_RANK'])43    44    elif 'SLURM_PROCID' in os.environ:45        args.rank = int(os.environ['SLURM_PROCID'])46        args.gpu = args.rank % torch.cuda.device_count()47    48    if args.world_size == 1:49        return50 51    if 'MASTER_ADDR' in os.environ:52        args.dist_url = 'tcp://{}:{}'.format(os.environ['MASTER_ADDR'], os.environ['MASTER_PORT'])53 54    print(f'gpu={args.gpu}, rank={args.rank}, world_size={args.world_size}')55    args.distributed = True56    torch.cuda.set_device(args.gpu)57    args.dist_backend = 'nccl'58    print('| distributed init (rank {}): {}'.format(args.rank, args.dist_url), flush=True)59    60    torch.distributed.init_process_group(backend=args.dist_backend, init_method=args.dist_url,61                                         world_size=args.world_size, rank=args.rank)62    torch.distributed.barrier()63 64 65def gather_list_and_concat(tensor):66    gather_t = [torch.ones_like(tensor) for _ in range(dist.get_world_size())]67    dist.all_gather(gather_t, tensor)68    return torch.cat(gather_t)69 70 71def get_rank():72    return dist.get_rank()73 74 75def get_world_size():76    return dist.get_world_size()77 78 79def get_default_group():80    return dist.group.WORLD81 82 83def all_gather_list(data, group=None, max_size=16384):84    """Gathers arbitrary data from all nodes into a list.85 86    Similar to :func:`~torch.distributed.all_gather` but for arbitrary Python87    data. Note that *data* must be picklable.88 89    Args:90        data (Any): data from the local worker to be gathered on other workers91        group (optional): group of the collective92        max_size (int, optional): maximum size of the data to be gathered93            across workers94    """95    rank = get_rank()96    world_size = get_world_size()97 98    buffer_size = max_size * world_size99    if not hasattr(all_gather_list, '_buffer') or \100            all_gather_list._buffer.numel() < buffer_size:101        all_gather_list._buffer = torch.cuda.ByteTensor(buffer_size)102        all_gather_list._cpu_buffer = torch.ByteTensor(max_size).pin_memory()103    buffer = all_gather_list._buffer104    buffer.zero_()105    cpu_buffer = all_gather_list._cpu_buffer106 107    data = data.cpu()108    enc = pickle.dumps(data)109    enc_size = len(enc)110    header_size = 4  # size of header that contains the length of the encoded data111    size = header_size + enc_size112    if size > max_size:113        raise ValueError('encoded data size ({}) exceeds max_size ({})'.format(size, max_size))114 115    header = struct.pack(">I", enc_size)116    cpu_buffer[:size] = torch.ByteTensor(list(header + enc))117    start = rank * max_size118    buffer[start:start + size].copy_(cpu_buffer[:size])119 120    all_reduce(buffer, group=group)121 122    buffer = buffer.cpu()123    try:124        result = []125        for i in range(world_size):126            out_buffer = buffer[i * max_size:(i + 1) * max_size]127            enc_size, = struct.unpack(">I", bytes(out_buffer[:header_size].tolist()))128            if enc_size > 0:129                result.append(pickle.loads(bytes(out_buffer[header_size:header_size + enc_size].tolist())))130        return result131    except pickle.UnpicklingError:132        raise Exception(133            'Unable to unpickle data from other workers. all_gather_list requires all '134            'workers to enter the function together, so this error usually indicates '135            'that the workers have fallen out of sync somehow. Workers can fall out of '136            'sync if one of them runs out of memory, or if there are other conditions '137            'in your training script that can cause one worker to finish an epoch '138            'while other workers are still iterating over their portions of the data. '139            'Try rerunning with --ddp-backend=no_c10d and see if that helps.'140        )141 142 143def all_reduce_dict(144    data: Mapping[str, Any],145    device,146    group=None,147) -> Dict[str, Any]:148    """149    AllReduce a dictionary of values across workers. We separately150    reduce items that are already on the device and items on CPU for151    better performance.152 153    Args:154        data (Mapping[str, Any]): dictionary of data to all-reduce, but155            cannot be a nested dictionary156        device (torch.device): device for the reduction157        group (optional): group of the collective158    """159    data_keys = list(data.keys())160 161    # We want to separately reduce items that are already on the162    # device and items on CPU for performance reasons.163    cpu_data = OrderedDict()164    device_data = OrderedDict()165    for k in data_keys:166        t = data[k]167        if not torch.is_tensor(t):168            cpu_data[k] = torch.tensor(t, dtype=torch.double)169        elif t.device.type != device.type:170            cpu_data[k] = t.to(dtype=torch.double)171        else:172            device_data[k] = t.to(dtype=torch.double)173 174    def _all_reduce_dict(data: OrderedDict):175        if len(data) == 0:176            return data177        buf = torch.stack(list(data.values())).to(device=device)178        all_reduce(buf, group=group)179        return {k: buf[i] for i, k in enumerate(data)}180 181    cpu_data = _all_reduce_dict(cpu_data)182    device_data = _all_reduce_dict(device_data)183 184    def get_from_stack(key):185        if key in cpu_data:186            return cpu_data[key]187        elif key in device_data:188            return device_data[key]189        raise KeyError190 191    return OrderedDict([(key, get_from_stack(key)) for key in data_keys])192 193 194def get_shared_folder() -> Path:195    user = os.getenv("USER")196    if Path("/checkpoint/").is_dir():197        p = Path(f"/checkpoint/{user}/experiments")198        p.mkdir(exist_ok=True)199        return p200    else:201        p = Path(f"/tmp/experiments")202        p.mkdir(exist_ok=True)203        return p204 205 206def get_init_file():207    # Init file must not exist, but it's parent dir must exist.208    os.makedirs(str(get_shared_folder()), exist_ok=True)209    init_file = Path(str(get_shared_folder()) + f"/{uuid.uuid4().hex}_init")210    if init_file.exists():211        os.remove(str(init_file))212    return init_file213 214