summarize_from_feedback/model_layout.py (46 lines of code) (raw):
import math
import numpy as np
class ModelLayout:
"""Holds the structure of the model and the current rank's position within it"""
@classmethod
def standard(cls, *, total_gpus, my_rank, n_shards=1):
assert my_rank < total_gpus, f"Bad rank {my_rank} for total_gpus = {total_gpus}"
ranks = np.arange(0, total_gpus)
gpus_per_replica = n_shards
assert (
total_gpus % gpus_per_replica == 0
), f"Total GPUs ({total_gpus}) is not divisible by {gpus_per_replica}"
replicas = total_gpus // gpus_per_replica
layout_np = np.reshape(ranks, [replicas, n_shards])
return cls(layout_np, my_rank)
def __eq__(self, other):
if not isinstance(other, ModelLayout):
return False
if self.my_rank != other.my_rank:
return False
return np.array_equal(self.layout, other.layout)
def __hash__(self):
# Best way to hash a numpy array according to stack overflow
# https://stackoverflow.com/a/16592241/610785
return hash((self.layout.tostring(), self.my_rank))
def __init__(self, layout, my_rank):
"""Layout is a numpy array with replica, shard"""
self.layout = layout
self.my_rank = my_rank
self.total_gpus = layout.size
self.all_ranks = list(range(self.total_gpus))
self.n_replicas, self.n_shards = layout.shape
if self.n_shards == 4:
print(
"WARNING: Using n_shards == 4 is currently slow because we have not"
"implemented an efficient ring following the [0,1,3,2] pattern"
)
([replica_idx], [shard_idx]) = np.where(layout == my_rank)
self.replica_idx = int(replica_idx)
self.shard_idx = int(shard_idx)
# Create convenient accessors
self.dp_sibling_ranks = [replica[shard_idx] for replica in layout]
self.mp_sibling_ranks = list(layout[replica_idx])
self.ranks_in_my_replica = layout[replica_idx].flatten().tolist()
self.is_in_first_replica = self.replica_idx == 0
self.replica_root = self.ranks_in_my_replica[0]
self.is_replica_root = self.replica_root == self.my_rank
self.is_logging_rank = self.is_replica_root and self.replica_idx == 0
self.ranks_on_my_node = list(
range(math.floor(self.my_rank / 8) * 8, 8 + math.floor(self.my_rank / 8) * 8)
)