illustrated-cluster / tests /trainingClusterModel.test.ts
joeddav's picture
Publish WIP HF Space snapshot
1f77aa7
Raw
History Blame Contribute Delete
6.67 kB
import { describe, expect, it } from 'vitest'
import {
a100_80gb,
analyzeCluster,
b300,
cluster64GPU,
h100_sxm,
llama70B,
llama7B,
llama31_405B,
olmo3_32B,
singleNode8GPU,
trinityLarge400B,
type ClusterConfig,
type TrainingConfig,
} from '../src/lib/trainingClusterModel'
const baselineTraining: TrainingConfig = {
microBatchSize: 1,
seqLength: 2048,
gradAccumSteps: 8,
precision: 'bf16',
activationCheckpointing: true,
optimizer: 'adamw',
}
describe('trainingClusterModel', () => {
it('fits Llama 2 7B on 8x A100 80GB with TP=8 and derived DP=1', () => {
const analysis = analyzeCluster(llama7B(), baselineTraining, singleNode8GPU(a100_80gb()), {
tp: 8,
pp: 1,
cp: 1,
ep: 1,
distributedOptimizer: false,
fsdpShardGroupSize: 0,
zeroStage: 0,
})
expect(analysis.feasible).toBe(true)
expect(analysis.derivedParallelism.dp).toBe(1)
expect(analysis.memoryBreakdown.totalGB).toBeLessThan(80)
})
it('marks Llama 2 70B on 8x A100 80GB as infeasible for unsharded Adam training', () => {
const analysis = analyzeCluster(llama70B(), baselineTraining, singleNode8GPU(a100_80gb()), {
tp: 8,
pp: 1,
cp: 1,
ep: 1,
distributedOptimizer: false,
fsdpShardGroupSize: 0,
zeroStage: 0,
})
expect(analysis.feasible).toBe(false)
expect(analysis.infeasibilityReason).toContain('exceeding 80 GB of HBM')
})
it('keeps MFU in a realistic range for a balanced 64x H100 dense run', () => {
const analysis = analyzeCluster(
llama70B(),
{
...baselineTraining,
seqLength: 4096,
gradAccumSteps: 16,
},
cluster64GPU(h100_sxm()),
{
tp: 4,
pp: 4,
cp: 1,
ep: 1,
distributedOptimizer: true,
fsdpShardGroupSize: 0,
zeroStage: 1,
},
)
expect(analysis.feasible).toBe(true)
expect(analysis.derivedParallelism.dp).toBe(4)
expect(analysis.throughput.mfu).toBeGreaterThan(0.3)
expect(analysis.throughput.mfu).toBeLessThanOrEqual(0.62)
})
it('reduces activation memory when CP increases and adds CP communication', () => {
const withoutCp = analyzeCluster(
llama70B(),
{
...baselineTraining,
seqLength: 4096,
},
cluster64GPU(h100_sxm()),
{
tp: 2,
pp: 2,
cp: 1,
ep: 1,
distributedOptimizer: true,
fsdpShardGroupSize: 0,
zeroStage: 1,
},
)
const withCp = analyzeCluster(
llama70B(),
{
...baselineTraining,
seqLength: 4096,
},
cluster64GPU(h100_sxm()),
{
tp: 2,
pp: 2,
cp: 4,
ep: 1,
distributedOptimizer: true,
fsdpShardGroupSize: 0,
zeroStage: 1,
},
)
expect(withCp.memoryBreakdown.activationsGB).toBeLessThan(withoutCp.memoryBreakdown.activationsGB)
expect(withCp.communication.cp.totalVolumePerStepGB).toBeGreaterThan(0)
})
it('reduces OLMo memory with HSDP shard groups compared with plain DP', () => {
const cluster = {
...cluster64GPU(h100_sxm()),
numNodes: 128,
nodesPerRack: 16,
}
const plain = analyzeCluster(
olmo3_32B(),
{
microBatchSize: 1,
seqLength: 8192,
gradAccumSteps: 1,
precision: 'bf16',
activationCheckpointing: true,
optimizer: 'adamw',
},
cluster,
{
tp: 1,
pp: 1,
cp: 1,
ep: 1,
distributedOptimizer: false,
fsdpShardGroupSize: 0,
zeroStage: 0,
},
)
const hsdp = analyzeCluster(
olmo3_32B(),
{
microBatchSize: 1,
seqLength: 8192,
gradAccumSteps: 1,
precision: 'bf16',
activationCheckpointing: true,
optimizer: 'adamw',
},
cluster,
{
tp: 1,
pp: 1,
cp: 1,
ep: 1,
distributedOptimizer: true,
fsdpShardGroupSize: 256,
zeroStage: 3,
},
)
expect(hsdp.derivedParallelism.replicaGroups).toBe(4)
expect(hsdp.memoryBreakdown.totalGB).toBeLessThan(plain.memoryBreakdown.totalGB)
expect(hsdp.communication.fsdp.totalVolumePerStepGB).toBeGreaterThan(0)
})
it('models Trinity as total-parameter-heavy but active-compute-light', () => {
const analysis = analyzeCluster(
trinityLarge400B(),
{
microBatchSize: 1,
seqLength: 8192,
gradAccumSteps: 8,
precision: 'bf16',
activationCheckpointing: true,
optimizer: 'muon',
},
trinityCluster(),
{
tp: 1,
pp: 1,
cp: 1,
ep: 8,
distributedOptimizer: true,
fsdpShardGroupSize: 128,
zeroStage: 3,
},
)
expect(analysis.totalParams).toBeGreaterThan(300_000_000_000)
expect(analysis.activeParamsPerToken).toBe(13_000_000_000)
expect(analysis.communication.ep.totalVolumePerStepGB).toBeGreaterThan(0)
expect(analysis.communication.ep.usesInterNode).toBe(false)
expect(new Set(analysis.gpuMap.map((gpu) => gpu.epLane))).toEqual(
new Set([0, 1, 2, 3, 4, 5, 6, 7]),
)
})
it('derives DP for Llama 3.1 405B from world size and 4D parallelism', () => {
const analysis = analyzeCluster(
llama31_405B(),
{
microBatchSize: 1,
seqLength: 8192,
gradAccumSteps: 16,
precision: 'bf16',
activationCheckpointing: true,
optimizer: 'adamw',
},
llama405Cluster(),
{
tp: 8,
pp: 16,
cp: 1,
ep: 1,
distributedOptimizer: true,
fsdpShardGroupSize: 0,
zeroStage: 1,
},
)
expect(analysis.derivedParallelism.dp).toBe(128)
expect(analysis.feasible).toBe(true)
expect(analysis.communication.tp.totalVolumePerStepGB).toBeGreaterThan(0)
expect(analysis.communication.pp.totalVolumePerStepGB).toBeGreaterThan(0)
expect(analysis.communication.fsdp.totalVolumePerStepGB).toBe(0)
})
})
function llama405Cluster(): ClusterConfig {
return {
gpuType: h100_sxm(),
gpusPerNode: 8,
numNodes: 2048,
intraNodeBandwidthGBs: 900,
interNodeBandwidthGBs: 50,
nodesPerRack: 16,
rackLabel: 'rack',
nodeLabel: 'GPU host',
podLabel: 'rack',
}
}
function trinityCluster(): ClusterConfig {
return {
gpuType: b300(),
gpusPerNode: 8,
numNodes: 256,
intraNodeBandwidthGBs: 900,
interNodeBandwidthGBs: 50,
nodesPerRack: 9,
rackLabel: 'rack',
nodeLabel: 'GPU host',
podLabel: 'rack',
}
}