Compute-Optimal Training › Training Efficiency in Practice
Language AI HandbookAll chapters Compute-Optimal Training › Training Efficiency in PracticeOne section · Sections in this chapter5 / 11 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Compute-optimal formulas assume you extract maximum value from each FLOP. In practice, many factors affect training efficiency, and optimizing these factors can significantly impact your effective compute budget. The gap between theoretical compute (what the formulas predict) and practical compute (what you achieve) can be substantial. Understanding this gap is essential for realistic planning and for identifying opportunities to improve training efficiency.
Model FLOPs Utilization
Model FLOPs Utilization (MFU) measures what fraction of theoretical peak hardware performance you achieve. This metric captures the cumulative effect of all inefficiencies in a training system, from memory bandwidth bottlenecks to communication overhead to operations that cannot fully utilize specialized hardware:
where:
- : Model FLOPs Utilization, a ratio between 0 and 1 (often expressed as a percentage)
- : the actual floating-point operations per second achieved during training
- : the theoretical maximum operations per second the hardware can perform
For example, an A100 GPU has a peak of 312 TFLOPS for FP16 tensor operations. If your training achieves 140 TFLOPS of actual compute, your MFU is . The gap comes from memory bandwidth limitations, communication overhead, and operations that cannot fully utilize tensor cores. This 55% gap might seem wasteful, but it reflects real constraints. Moving data between GPU memory and compute units takes time; synchronizing gradients across GPUs requires communication; and not every operation in a transformer maps efficiently onto tensor cores.
Typical MFU values for LLM training range from 30% to 60%, depending on:
- Hardware configuration: Multi-GPU communication overhead reduces MFU as cluster size grows
- Batch size: Larger batches amortize fixed costs but may require more memory
- Model architecture: Some operations (LayerNorm, activations) are memory-bound rather than compute-bound
- Precision: Mixed precision training (FP16/BF16 with FP32 accumulation) typically achieves higher MFU than FP32
Each of these factors represents an engineering challenge with its own optimization strategies. Hardware communication overhead can be reduced through careful placement of operations, overlapping communication with computation, and using efficient collective primitives. Batch size choices involve tradeoffs between memory pressure, gradient quality, and throughput. Architecture decisions affect the ratio of compute-bound to memory-bound operations, with implications for how efficiently the hardware can be utilized.
def effective_compute_cost(
target_params: float,
target_tokens: float,
mfu: float,
num_gpus: int,
gpu_tflops: float = 312.0,
) -> dict:
"""
Calculate training time given MFU and hardware constraints.
"""
# Total required FLOPs
required_flops = 6 * target_params * target_tokens
# Effective throughput
effective_tflops = gpu_tflops * mfu * num_gpus
effective_flops_per_second = effective_tflops * 1e12
# Training time
training_seconds = required_flops / effective_flops_per_second
training_hours = training_seconds / 3600
training_days = training_hours / 24
return {
"required_flops": required_flops,
"throughput_tflops": effective_tflops,
"training_hours": training_hours,
"training_days": training_days,
}
# Compare different MFU scenarios
mfu_scenarios = [0.30, 0.45, 0.60]
target_n = 7e9 # 7B model
target_d = 140e9 # 140B tokens (20x parameters)Training a 7B model on 140B tokens Using 128 A100 GPUs (312 TFLOPS each) MFU = 30%: Effective throughput: 11981 TFLOPS Training time: 5.7 days MFU = 45%: Effective throughput: 17971 TFLOPS Training time: 3.8 days MFU = 60%: Effective throughput: 23962 TFLOPS Training time: 2.8 days

A 30% improvement in MFU (from 30% to 60%) cuts training time in half. This makes MFU optimization a critical concern for compute-optimal training. The practical implication is that engineering effort invested in improving MFU pays dividends equivalent to acquiring additional hardware. Improving MFU from 30% to 60% is equivalent to doubling your GPU count at no additional hardware cost.
Learning Rate Schedules
Learning rate scheduling significantly affects training efficiency. The learning rate controls how aggressively the optimizer updates model parameters in response to gradient signals. Too high, and training becomes unstable as updates overshoot optimal parameter values. Too low, and training proceeds sluggishly, wasting compute on inefficient updates. The standard approach for LLM training uses:
- Warmup: Linear increase from near-zero to peak learning rate
- Decay: Cosine or linear decay to a minimum value
The warmup phase serves a critical stability function. At initialization, model parameters are random and gradients can be unreliable. Large learning rates applied to noisy gradients cause training to diverge. By starting with small learning rates and gradually increasing, warmup allows the model to enter a stable region before aggressive optimization begins. The decay phase addresses a different concern: as training progresses and the model approaches a good solution, large learning rates cause the optimizer to overshoot and oscillate around the minimum. Gradually reducing the learning rate allows fine-grained convergence.
The key parameters are:
- Peak learning rate: Scales inversely with model size; larger models use smaller learning rates
- Warmup steps: Typically 0.1% to 1% of total training steps
- Minimum learning rate: Usually 10% of peak, though some schedules decay to zero
import numpy as np
def cosine_schedule(
step: int,
total_steps: int,
warmup_steps: int,
peak_lr: float,
min_lr_ratio: float = 0.1,
) -> float:
"""Cosine learning rate schedule with warmup."""
if step < warmup_steps:
# Linear warmup
return peak_lr * step / warmup_steps
else:
# Cosine decay
progress = (step - warmup_steps) / (total_steps - warmup_steps)
cosine_decay = 0.5 * (1 + np.cos(np.pi * progress))
min_lr = peak_lr * min_lr_ratio
return min_lr + (peak_lr - min_lr) * cosine_decay
# Typical schedule for a 7B model
total_steps = 20000
warmup_steps = 2000
peak_lr = 3e-4 # Common for 7B scale
steps = np.arange(total_steps)
lrs = [cosine_schedule(s, total_steps, warmup_steps, peak_lr) for s in steps]
Batch Size Scaling
Batch size affects both training dynamics and compute efficiency. Understanding batch size requires recognizing that gradient descent operates on estimated gradients. Each batch provides a noisy estimate of the true gradient, with larger batches providing more accurate estimates at the cost of more computation per update. The critical batch size is the point beyond which larger batches provide diminishing returns. Below this threshold, doubling batch size nearly halves training time. Above it, larger batches waste compute on redundant gradient information.
The diminishing returns arise from the statistical nature of gradient estimation. With small batches, each sample contributes significant independent information about the gradient direction. As batch size grows, samples begin to provide redundant information, they agree on the gradient direction, so averaging more of them improves the estimate only marginally. The critical batch size marks the transition between these regimes.
Empirically, the critical batch size scales with loss. As training progresses, the gradient signal becomes noisier relative to its magnitude, meaning that averaging over more samples (larger batches) provides diminishing returns. The relationship is:
where:
- : the critical batch size, beyond which larger batches provide diminishing returns
- : the inverse of the current loss value
This inverse relationship has an intuitive explanation: when loss is high (early in training), gradients are large and consistent across samples, so even small batches provide reliable gradient estimates. As loss decreases and the model approaches convergence, gradients become smaller and noisier, requiring more samples to average out the noise and get a reliable signal. This is why early in training (when loss is high), smaller batches are optimal. As the model improves and loss drops, it can benefit from larger batches.
Think of it this way: early in training, the model is making large, obvious mistakes that any sample can reveal. A small batch suffices to identify that "the model thinks 'the' should follow 'the', and that's wrong." Later in training, the model's errors are subtle, and any individual sample might push the gradient in a misleading direction due to noise. Larger batches average out these idiosyncratic signals to reveal the true underlying gradient.


As training progresses and loss decreases, the critical batch size increases. This motivates batch size warmup strategies that start with smaller batches and gradually increase throughout training.
For compute-optimal training, practitioners typically:
- Start with batch sizes of 256K to 1M tokens
- Increase batch size 2-4x during training if the schedule allows
- Use gradient accumulation to achieve large effective batch sizes on limited hardware