Files
2026-07-13 13:18:33 +08:00

105 lines
4.9 KiB
Python

# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
from typing import Optional, Tuple
import torch
from .types import ShardingType, DEFAULT_SHARD_GRANULARITY
def get_shard_endpoints(dim_size: int,
shard_rank: int,
num_shards: int,
granularity: int = DEFAULT_SHARD_GRANULARITY) -> Tuple[int, int]:
"""
Given a dimension to shard with size dim_size, return the start and end indices of the slice
that belong to the given rank.
The typical use of this is as an internal helper function, so see if there is a higher level
API that better suits the application.
Args:
dim_size (int): The size of the dimension to shard.
shard_rank (int): The rank of the shard to return.
num_shards (int): Total number of shards the dimension will be distributed across.
granularity (int): The minimum alignment of the shard endpoints. This is used to support
non-even head counts as well as align dimensions to cleaner GEMM boundaries.
"""
assert dim_size % granularity == 0, "Dimension size must be divisible by granularity"
total_chunks = dim_size // granularity
base_chunks_per_rank = total_chunks // num_shards
remainder_chunks = total_chunks % num_shards
start_chunk_id = shard_rank * base_chunks_per_rank + min(shard_rank, remainder_chunks)
end_chunk_id = start_chunk_id + base_chunks_per_rank + (1 if shard_rank < remainder_chunks else 0)
return start_chunk_id * granularity, end_chunk_id * granularity
def shard_param(param: Optional[torch.Tensor],
shard_mode: ShardingType,
shard_rank: int,
num_shards: int,
num_concatenated_matrices: int = 1,
granularity: int = 32,
bias_dims: int = 1) -> torch.Tensor:
"""
Utility for sharding a parameter. This will return the slice of the parameter that should
exist on the given shard_rank given the sharding configuration. The workflow here is
to find the minimum bounded Tensor to shard, get the slicing endpoints, and then concatenate
as needed.
The typical use of this is as an internal helper function, so see if there is a higher level
API that better suits the application.
Args:
param (torch.Tensor): The parameter to shard.
shard_mode (ShardingType): The type of sharding to apply. See ShardingType for more context.
shard_rank (int): The rank of the shard to return.
num_shards (int): Total number of shards the parameter will be distrbuted across.
num_concatenated_matrices (int): The number of matrices that have been concatenated together in the original
parameter. An example of this is a fused QKV projection matrix, where the `num_concatenated_matrices`
argument would be 3.
granularity (int): The minimum alignment of the shard endpoints. For attention projection matrices, this
should be set to the head size to support non-even sharding.
bias_dims (int): The number of dimensions that are considered bias dimensions. This is used to support
sharding of MoE and non-MoE biases on the same codepath.
"""
assert shard_rank < num_shards, "Shard rank must be less than num_shards"
# Easier to hide this inside of the sharding logic than to add checks in every model
# implementation.
if param is None:
return None
if num_shards == 1:
# Trivial case of no sharding.
return param
if shard_mode == ShardingType.OUTER_DIMENSION:
def get_matrices(dim_idx: int) -> torch.Tensor:
dim_size = param.size(dim_idx) // num_concatenated_matrices
start_channel_id, end_channel_id = get_shard_endpoints(dim_size, shard_rank, num_shards, granularity)
return torch.chunk(param, num_concatenated_matrices, dim=dim_idx), start_channel_id, end_channel_id
if param.ndim == bias_dims:
# Special case for bias parameters.
matrices, start_channel_id, end_channel_id = get_matrices(dim_idx=-1)
return torch.cat([mat[..., start_channel_id:end_channel_id] for mat in matrices], dim=-1)
else:
# General case for weight parameters. This assumes MoE parameters are stored in the format of
# [num_experts, out_features, in_features]
matrices, start_channel_id, end_channel_id = get_matrices(dim_idx=-2)
return torch.cat([mat[..., start_channel_id:end_channel_id, :] for mat in matrices], dim=-2)
elif shard_mode == ShardingType.INNER_DIMENSION:
dim_size = param.size(-1) // num_concatenated_matrices
start_channel_id, end_channel_id = get_shard_endpoints(dim_size, shard_rank, num_shards, granularity)
matrices = torch.chunk(param, num_concatenated_matrices, dim=-1)
return torch.cat([mat[..., start_channel_id:end_channel_id] for mat in matrices], dim=-1)