Approach: compute each component in bytes from inputs, sum, and convert to GB. We treat:- weights = P * bytes_per_param- gradients = same size as weights- optimizer = optimizer_multiplier * weights- activations = bytes_per_activation_per_sample * batch_size- scratch = scratch_multiplier * (weights + activations) — a common heuristic for temp buffers (adjustable)Here's a concise, well-documented implementation and notes.python
def estimate_gpu_memory_gb(
P,
bytes_per_activation_per_sample,
batch_size,
optimizer_multiplier=2.0,
bytes_per_param=4,
scratch_multiplier=0.1,
):
"""
Estimate GPU memory usage.
Args:
P: int - number of model parameters
bytes_per_activation_per_sample: float - bytes used by activations per sample
batch_size: int
optimizer_multiplier: float - e.g., Adam ~= 2 (moment estimates) or 1 for SGD
bytes_per_param: int - bytes per parameter (4 for float32, 2 for float16)
scratch_multiplier: float - fraction of (weights + activations) reserved for temporary buffers
Returns:
dict with:
total_gb: float
breakdown_bytes: dict of component sizes in bytes
breakdown_gb: dict of component sizes in GB
"""
if P < 0 or batch_size < 0 or bytes_per_activation_per_sample < 0:
raise ValueError("P, batch_size and bytes_per_activation_per_sample must be non-negative")
# Components in bytes
weights = int(P * bytes_per_param)
gradients = int(weights) # typically one gradient tensor same dtype as params
optimizer = int(optimizer_multiplier * weights) # optimizer state (moments, etc.)
activations = int(bytes_per_activation_per_sample * batch_size)
scratch = int(scratch_multiplier * (weights + activations))
total_bytes = weights + gradients + optimizer + activations + scratch
def to_gb(b): return b / (1024 ** 3)
breakdown_bytes = {
"weights": weights,
"gradients": gradients,
"optimizer": optimizer,
"activations": activations,
"scratch": scratch,
}
breakdown_gb = {k: to_gb(v) for k, v in breakdown_bytes.items()}
return {
"total_gb": to_gb(total_bytes),
"breakdown_bytes": breakdown_bytes,
"breakdown_gb": breakdown_gb,
}
# Example:
# estimate_gpu_memory_gb(P=1_000_000_000, bytes_per_activation_per_sample=2000, batch_size=32, optimizer_multiplier=2, bytes_per_param=4, scratch_multiplier=0.1)
Key points:- Units use bytes -> GB (GiB) via 1024**3.- Heuristic choices: gradients equal to weights, optimizer_multiplier depends on optimizer (Adam ~2 for two moment tensors, plus possibly more), scratch_multiplier is adjustable.- Time/space complexity: O(1) arithmetic.Edge cases:- Very large P may overflow Python int? Python ints are arbitrary precision, but watch downstream APIs.- If using ZeRO/partitioning, optimizer/gradients may be reduced — adjust multipliers accordingly.Alternative: make activations computed per-layer structure if you have model topology instead of a flat per-sample estimate.