mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-07-25 05:50:17 -05:00
Migrate Vol I remaining chapters from .m_as() to .to().magnitude.
Covers model_compression through conclusion plus Vol I appendices; fixes byte-vs-Bparam and missing USD/kg/param imports surfaced by exec tests.
This commit is contained in:
@@ -78,11 +78,11 @@ class GemmIntensityExample:
|
||||
n_small = 64
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
bytes_fp16 = int(BYTES_FP16.m_as(ureg.byte))
|
||||
bytes_fp16 = int(BYTES_FP16.to(byte).magnitude)
|
||||
small_intensity = n_small / 3
|
||||
a100_ridge = int(
|
||||
Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
/ Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
/ Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
)
|
||||
small_efficiency_pct = small_intensity / a100_ridge * 100
|
||||
|
||||
@@ -140,14 +140,14 @@ class SparseEmbeddingExample:
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
total_elements = vocab_size * embed_dim
|
||||
dense_bytes_q = total_elements * BYTES_FP32
|
||||
dense_bytes = dense_bytes_q.m_as(byte)
|
||||
dense_gb = dense_bytes_q.m_as(GB)
|
||||
dense_bytes = dense_bytes_q.to(byte).magnitude
|
||||
dense_gb = dense_bytes_q.to(GB).magnitude
|
||||
nonzeros = int(total_elements * sparsity_pct / 100)
|
||||
csr_bytes_per_nonzero_q = BYTES_FP32 + BYTES_INT32
|
||||
csr_bytes_per_nonzero = csr_bytes_per_nonzero_q.m_as(byte)
|
||||
csr_bytes_per_nonzero = csr_bytes_per_nonzero_q.to(byte).magnitude
|
||||
sparse_bytes_q = nonzeros * csr_bytes_per_nonzero_q
|
||||
sparse_bytes = sparse_bytes_q.m_as(byte)
|
||||
sparse_mb = sparse_bytes_q.m_as(MB)
|
||||
sparse_bytes = sparse_bytes_q.to(byte).magnitude
|
||||
sparse_mb = sparse_bytes_q.to(MB).magnitude
|
||||
reduction_factor = int(dense_bytes / sparse_bytes)
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -157,8 +157,8 @@ class SparseEmbeddingExample:
|
||||
dense_gb_str = fmt_qty(dense_bytes_q, GB, precision=0, commas=False)
|
||||
nonzeros_str = fmt(nonzeros / MILLION, precision=0, commas=False)
|
||||
sparse_mb_str = fmt_qty(sparse_bytes_q, MB, precision=0, commas=False)
|
||||
bytes_fp32_str = fmt_int(BYTES_FP32.m_as(ureg.byte), commas=False)
|
||||
bytes_int32_str = fmt_int(BYTES_INT32.m_as(ureg.byte), commas=False)
|
||||
bytes_fp32_str = fmt_int(BYTES_FP32.to(byte).magnitude, commas=False)
|
||||
bytes_int32_str = fmt_int(BYTES_INT32.to(byte).magnitude, commas=False)
|
||||
sparsity_pct_str = fmt_percent(sparsity_pct/100, precision=0, commas=False, style='prose')
|
||||
reduction_factor_str = fmt(reduction_factor, precision=0, commas=False)
|
||||
```
|
||||
@@ -314,10 +314,10 @@ from mlsysim.fmt import fmt_int, fmt, MarkdownStr
|
||||
|
||||
class TrainingMemoryBytes:
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
bytes_fp16_str = fmt_int(BYTES_FP16.m_as(ureg.byte), commas=False)
|
||||
bytes_fp32_str = fmt_int(BYTES_FP32.m_as(ureg.byte), commas=False)
|
||||
optimizer_overhead_min = int(BYTES_ADAM_STATE.m_as(ureg.byte))
|
||||
optimizer_overhead_max = optimizer_overhead_min + int(BYTES_FP32.m_as(ureg.byte))
|
||||
bytes_fp16_str = fmt_int(BYTES_FP16.to(byte).magnitude, commas=False)
|
||||
bytes_fp32_str = fmt_int(BYTES_FP32.to(byte).magnitude, commas=False)
|
||||
optimizer_overhead_min = int(BYTES_ADAM_STATE.to(byte).magnitude)
|
||||
optimizer_overhead_max = optimizer_overhead_min + int(BYTES_FP32.to(byte).magnitude)
|
||||
optimizer_overhead_str = MarkdownStr(f"{optimizer_overhead_min}–{optimizer_overhead_max}")
|
||||
```
|
||||
|
||||
@@ -351,48 +351,48 @@ To see how these components interact in practice, consider a concrete model.
|
||||
# │ gpt2_act_small_gb_str, gpt2_total_small_gb_str,
|
||||
# │ gpt2_batch_large_str, gpt2_act_large_gb_str
|
||||
# └─────────────────────────────────────────────────────────────────────────────
|
||||
from mlsysim.fmt import fmt_int, fmt, check, sci_latex, fmt_math, MarkdownStr, fmt_percent, fmt_qty
|
||||
from mlsysim.fmt import fmt_int, fmt, check, sci_latex, fmt_math, fmt_percent, fmt_qty
|
||||
|
||||
class GPT2TrainingMem:
|
||||
# ┌── 1. LOAD ──────────────────────────────────────────
|
||||
P = Models.Language.GPT2.parameters.m_as(param)
|
||||
P = Models.Language.GPT2.parameters.to(param).magnitude
|
||||
layers = Models.Language.GPT2.layers
|
||||
d = Models.Language.GPT2.hidden_dim
|
||||
bf16 = int(BYTES_FP16.m_as(ureg.byte))
|
||||
fp32 = int(BYTES_FP32.m_as(ureg.byte))
|
||||
bf16 = int(BYTES_FP16.to(byte).magnitude)
|
||||
fp32 = int(BYTES_FP32.to(byte).magnitude)
|
||||
accel_q = Hardware.Cloud.A100.memory.capacity
|
||||
accel_gb = accel_q.m_as(GB)
|
||||
accel_gb = accel_q.to(GB).magnitude
|
||||
batch_small = 8
|
||||
batch_large = 64
|
||||
seq_len = 1024
|
||||
optimizer_bytes_per_param = int(
|
||||
(BYTES_ADAM_STATE + BYTES_FP32).m_as(ureg.byte)
|
||||
(BYTES_ADAM_STATE + BYTES_FP32).to(byte).magnitude
|
||||
) # FP32 master + momentum + variance
|
||||
|
||||
# ┌── 2. EXECUTE ───────────────────────────────────────
|
||||
weights_q = P * bf16 * byte
|
||||
grads_q = P * bf16 * byte
|
||||
optim_q = P * optimizer_bytes_per_param * byte
|
||||
weights_gb = weights_q.m_as(GB)
|
||||
grads_gb = grads_q.m_as(GB)
|
||||
optim_gb = optim_q.m_as(GB)
|
||||
weights_gb = weights_q.to(GB).magnitude
|
||||
grads_gb = grads_q.to(GB).magnitude
|
||||
optim_gb = optim_q.to(GB).magnitude
|
||||
model_state_q = weights_q + grads_q + optim_q
|
||||
model_state_gb = model_state_q.m_as(GB)
|
||||
model_state_gb = model_state_q.to(GB).magnitude
|
||||
remaining_q = accel_q - model_state_q
|
||||
remaining_gb = remaining_q.m_as(GB)
|
||||
remaining_gb = remaining_q.to(GB).magnitude
|
||||
|
||||
# Per-layer activation ≈ 12 * B * S * d * bytes_bf16:
|
||||
# input(1d) + QKV(3d) + attn_out(1d) + FFN(4d) + output(1d) + norms/masks(2d)
|
||||
act_factor = 12
|
||||
act_small_q = layers * act_factor * batch_small * seq_len * d * bf16 * byte
|
||||
act_small_bytes = act_small_q.m_as(byte)
|
||||
act_small_gb = act_small_q.m_as(GB)
|
||||
act_small_bytes = act_small_q.to(byte).magnitude
|
||||
act_small_gb = act_small_q.to(GB).magnitude
|
||||
total_small_q = model_state_q + act_small_q
|
||||
total_small_gb = total_small_q.m_as(GB)
|
||||
total_small_gb = total_small_q.to(GB).magnitude
|
||||
|
||||
act_large_q = layers * act_factor * batch_large * seq_len * d * bf16 * byte
|
||||
act_large_bytes = act_large_q.m_as(byte)
|
||||
act_large_gb = act_large_q.m_as(GB)
|
||||
act_large_bytes = act_large_q.to(byte).magnitude
|
||||
act_large_gb = act_large_q.to(GB).magnitude
|
||||
|
||||
# ┌── 3. GUARD ─────────────────────────────────────────
|
||||
check(model_state_gb < accel_gb, "Model state must fit on accelerator")
|
||||
@@ -400,7 +400,7 @@ class GPT2TrainingMem:
|
||||
check(act_large_gb > remaining_gb, "Large batch should exceed remaining capacity")
|
||||
|
||||
# ┌── 4. OUTPUT ────────────────────────────────────────
|
||||
P_str = MarkdownStr(f"${sci_latex(P, precision=1)}$") # LaTeX-safe: 1.5 \times 10^{9} (avoids 1.5e+09)
|
||||
P_str = fmt_math(sci_latex(P, precision=1)) # LaTeX-safe: 1.5 \times 10^{9} (avoids 1.5e+09)
|
||||
layers_str = fmt(layers, precision=0, commas=False)
|
||||
d_str = fmt(d, precision=0, commas=False)
|
||||
bf16_str = fmt(bf16, precision=0, commas=False)
|
||||
@@ -490,10 +490,10 @@ from mlsysim.fmt import fmt
|
||||
|
||||
class SparseBreakEvenRecap:
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
csr_bytes_per_nonzero = BYTES_FP32.m_as(ureg.byte) + BYTES_INT32.m_as(ureg.byte)
|
||||
coo_bytes_per_nonzero = BYTES_FP32.m_as(ureg.byte) + 2 * BYTES_INT32.m_as(ureg.byte)
|
||||
csr_break_even_density_pct = BYTES_FP32.m_as(ureg.byte) / csr_bytes_per_nonzero * 100
|
||||
coo_break_even_density_pct = BYTES_FP32.m_as(ureg.byte) / coo_bytes_per_nonzero * 100
|
||||
csr_bytes_per_nonzero = BYTES_FP32.to(byte).magnitude + BYTES_INT32.to(byte).magnitude
|
||||
coo_bytes_per_nonzero = BYTES_FP32.to(byte).magnitude + 2 * BYTES_INT32.to(byte).magnitude
|
||||
csr_break_even_density_pct = BYTES_FP32.to(byte).magnitude / csr_bytes_per_nonzero * 100
|
||||
coo_break_even_density_pct = BYTES_FP32.to(byte).magnitude / coo_bytes_per_nonzero * 100
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
csr_break_even_density_pct_str = fmt_percent(csr_break_even_density_pct/100, precision=0, commas=False, style='prose')
|
||||
|
||||
@@ -51,13 +51,13 @@ class NapkinMath:
|
||||
h100_bw_q = Hardware.Cloud.H100.memory.bandwidth
|
||||
h100_mem_q = Hardware.Cloud.H100.memory.capacity
|
||||
a100_tdp_q = Hardware.Cloud.A100.tdp
|
||||
h100_tflops = h100_flops_q.m_as(TFLOPs/second)
|
||||
h100_bw_tbs = h100_bw_q.m_as(TB/second)
|
||||
h100_mem_gb = h100_mem_q.m_as(GB)
|
||||
a100_tdp_w = a100_tdp_q.m_as(watt)
|
||||
h100_tflops = h100_flops_q.to(TFLOPs/second).magnitude
|
||||
h100_bw_tbs = h100_bw_q.to(TB/second).magnitude
|
||||
h100_mem_gb = h100_mem_q.to(GB).magnitude
|
||||
a100_tdp_w = a100_tdp_q.to(watt).magnitude
|
||||
gpt3_ops = Models.Language.GPT3.training_ops.magnitude
|
||||
gpt3_days = Models.Language.GPT3.training_days_ref.magnitude
|
||||
gpt3_accelerators = Models.Language.GPT3.training_accelerators_ref.m_as(count)
|
||||
gpt3_accelerators = Models.Language.GPT3.training_accelerators_ref.to(count).magnitude
|
||||
elec_per_kwh = Infrastructure.Pricing.Cloud.ElectricityPerKwh.rate.magnitude
|
||||
|
||||
gemm_n = 4096
|
||||
@@ -67,15 +67,15 @@ class NapkinMath:
|
||||
+ BYTES_FP16 # BF16 gradients
|
||||
+ 3 * BYTES_FP32 # FP32 master weights + momentum + variance
|
||||
)
|
||||
bytes_per_param = bytes_per_param_q.m_as(byte)
|
||||
bytes_per_param = bytes_per_param_q.to(byte).magnitude
|
||||
hours_per_day = HOURS_PER_DAY
|
||||
|
||||
# ┌── 2. EXECUTE ───────────────────────────────────────
|
||||
ridge = h100_tflops/h100_bw_tbs
|
||||
gemm_intensity = gemm_n / 3
|
||||
train_mem_q = model_params_b * BILLION * bytes_per_param_q
|
||||
train_mem_gb = train_mem_q.m_as(GB)
|
||||
a100_tdp_kw = a100_tdp_q.m_as(kilowatt)
|
||||
train_mem_gb = train_mem_q.to(GB).magnitude
|
||||
a100_tdp_kw = a100_tdp_q.to(kW).magnitude
|
||||
elec_cost = gpt3_days * gpt3_accelerators * hours_per_day * a100_tdp_kw * elec_per_kwh
|
||||
|
||||
# ┌── 3. GUARD ─────────────────────────────────────────
|
||||
|
||||
@@ -53,11 +53,11 @@ from mlsysim import Systems # Ethernet fabric bandwidths
|
||||
|
||||
class DataGravity:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────
|
||||
tb_bytes = (1 * TB).m_as(byte)
|
||||
pb_bytes = (1 * PB).m_as(byte)
|
||||
bw_1g = (1 * Gbps).m_as('bit/second')
|
||||
bw_10g = Systems.Fabrics.Ethernet_10G.bandwidth.m_as('bit/second')
|
||||
bw_100g = Systems.Fabrics.Ethernet_100G.bandwidth.m_as('bit/second')
|
||||
tb_bytes = (1 * TB).to(byte).magnitude
|
||||
pb_bytes = (1 * PB).to(byte).magnitude
|
||||
bw_1g = (1 * Gbps).to(bit / second).magnitude
|
||||
bw_10g = Systems.Fabrics.Ethernet_10G.bandwidth.to(bit / second).magnitude
|
||||
bw_100g = Systems.Fabrics.Ethernet_100G.bandwidth.to(bit / second).magnitude
|
||||
truck_load_hours = 8
|
||||
truck_transit_hours = 32
|
||||
truck_unload_hours = 8
|
||||
@@ -159,12 +159,12 @@ class SerializationCost:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────
|
||||
csv_speed = 100 * MB/second
|
||||
parquet_speed = 1000 * MB/second
|
||||
csv_speed_mb = csv_speed.m_as(MB/second)
|
||||
parquet_speed_mb = parquet_speed.m_as(MB/second)
|
||||
csv_speed_mb = csv_speed.to(MB/second).magnitude
|
||||
parquet_speed_mb = parquet_speed.to(MB/second).magnitude
|
||||
csv_cycles = 100
|
||||
parquet_cycles = 10
|
||||
proto_speed = 300 * MB/second
|
||||
proto_speed_mb = proto_speed.m_as(MB/second)
|
||||
proto_speed_mb = proto_speed.to(MB/second).magnitude
|
||||
proto_cycles = 200
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────
|
||||
@@ -332,16 +332,16 @@ from mlsysim.fmt import fmt, check, fmt_percent, fmt_qty, MarkdownStr
|
||||
class DataAlgebra:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────
|
||||
row_size_q = 1 * KB
|
||||
row_size_kb = row_size_q.m_as(KB)
|
||||
int_size_bytes = BYTES_INT32.m_as(byte)
|
||||
row_size_kb = row_size_q.to(KB).magnitude
|
||||
int_size_bytes = BYTES_INT32.to(byte).magnitude
|
||||
join_table_q = 1 * TB
|
||||
join_table_tb = join_table_q.m_as(TB)
|
||||
join_table_tb = join_table_q.to(TB).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────
|
||||
row_size_bytes = row_size_q.m_as(byte)
|
||||
row_size_bytes = row_size_q.to(byte).magnitude
|
||||
waste_pct = (1 - (int_size_bytes / row_size_bytes)) * 100
|
||||
join_network_q = 2 * join_table_q
|
||||
join_network_tb = join_network_q.m_as(TB)
|
||||
join_network_tb = join_network_q.to(TB).magnitude
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ────────────────────────────
|
||||
check(waste_pct > 99, "Row-format waste should be >99%")
|
||||
|
||||
@@ -89,45 +89,45 @@ class NumbersToKnow:
|
||||
LATENCY_HBM3 = Hardware.Tech.Memory.DRAM.latency
|
||||
LATENCY_PCIE_GEN5 = Hardware.Tech.Interconnect.PCIeGen5.latency
|
||||
LATENCY_NVME_SSD = Hardware.Tech.Storage.NvmeGen4.latency
|
||||
LATENCY_INFINIBAND = round(Systems.Fabrics.InfiniBand_NDR.latency.m_as(NS)) * NS # µs→ns, round to exact 5000
|
||||
LATENCY_INFINIBAND = round(Systems.Fabrics.InfiniBand_NDR.latency.to(NS).magnitude) * NS # µs→ns, round to exact 5000
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
# Step 1: Physics Constants (Eternal)
|
||||
speed_of_light_km_ms = int(SPEED_OF_LIGHT_FIBER_KM_S.m_as(ureg.kilometer / ureg.millisecond))
|
||||
speed_of_light_km_ms = int(SPEED_OF_LIGHT_FIBER_KM_S.to(km / ms).magnitude)
|
||||
|
||||
# Step 2: Energy Ratios (Stable across process nodes)
|
||||
dram_vs_compute = int(ENERGY_DRAM_ACCESS_PJ.m_as(ureg.picojoule) / ENERGY_FLOP_FP16_PJ.m_as(ureg.picojoule))
|
||||
fp32_vs_int8 = int(ENERGY_FLOP_FP32_PJ.m_as(ureg.picojoule) / ENERGY_OP_INT8_PJ.m_as(ureg.picojoule))
|
||||
fp32_vs_fp16 = round(ENERGY_FLOP_FP32_PJ.m_as(ureg.picojoule) / ENERGY_FLOP_FP16_PJ.m_as(ureg.picojoule), 1)
|
||||
l1_vs_reg = int(ENERGY_SRAM_L1_PJ.m_as(ureg.picojoule) / ENERGY_REG_PJ.m_as(ureg.picojoule))
|
||||
dram_vs_compute = int(ENERGY_DRAM_ACCESS_PJ.to(pJ).magnitude / ENERGY_FLOP_FP16_PJ.to(pJ).magnitude)
|
||||
fp32_vs_int8 = int(ENERGY_FLOP_FP32_PJ.to(pJ).magnitude / ENERGY_OP_INT8_PJ.to(pJ).magnitude)
|
||||
fp32_vs_fp16 = round(ENERGY_FLOP_FP32_PJ.to(pJ).magnitude / ENERGY_FLOP_FP16_PJ.to(pJ).magnitude, 1)
|
||||
l1_vs_reg = int(ENERGY_SRAM_L1_PJ.to(pJ).magnitude / ENERGY_REG_PJ.to(pJ).magnitude)
|
||||
|
||||
# Step 3: Memory Hierarchy Ratios (Stable)
|
||||
register_latency_ns = LATENCY_REGISTER_REF.m_as(NS)
|
||||
hbm_vs_register = int(LATENCY_HBM3.m_as(NS) / register_latency_ns)
|
||||
ssd_vs_register = int(round((LATENCY_NVME_SSD.m_as(NS) / register_latency_ns) / 100_000) * 100_000)
|
||||
network_vs_local = int(LATENCY_INFINIBAND.m_as(NS) / LATENCY_HBM3.m_as(NS))
|
||||
gpu_bw_vs_pcie = int(h_h100.memory.bandwidth.m_as(GB/second) / Hardware.Cloud.H100.interconnect.bandwidth.m_as(GB/second))
|
||||
register_latency_ns = LATENCY_REGISTER_REF.to(NS).magnitude
|
||||
hbm_vs_register = int(LATENCY_HBM3.to(NS).magnitude / register_latency_ns)
|
||||
ssd_vs_register = int(round((LATENCY_NVME_SSD.to(NS).magnitude / register_latency_ns) / 100_000) * 100_000)
|
||||
network_vs_local = int(LATENCY_INFINIBAND.to(NS).magnitude / LATENCY_HBM3.to(NS).magnitude)
|
||||
gpu_bw_vs_pcie = int(h_h100.memory.bandwidth.to(GB/second).magnitude / Hardware.Cloud.H100.interconnect.bandwidth.to(GB/second).magnitude)
|
||||
|
||||
# Step 4: Current Hardware Reference (circa 2024)
|
||||
lat_l1_ns = int(LATENCY_L1_REGISTER.m_as(NS))
|
||||
lat_l2_ns = int(LATENCY_L2_CACHE.m_as(NS))
|
||||
lat_hbm_ns = int(LATENCY_HBM3.m_as(NS))
|
||||
lat_pcie_ns = int(LATENCY_PCIE_GEN5.m_as(NS))
|
||||
lat_ib_ns = int(LATENCY_INFINIBAND.m_as(NS))
|
||||
lat_ssd_ns = int(LATENCY_NVME_SSD.m_as(NS))
|
||||
lat_l1_ns = int(LATENCY_L1_REGISTER.to(NS).magnitude)
|
||||
lat_l2_ns = int(LATENCY_L2_CACHE.to(NS).magnitude)
|
||||
lat_hbm_ns = int(LATENCY_HBM3.to(NS).magnitude)
|
||||
lat_pcie_ns = int(LATENCY_PCIE_GEN5.to(NS).magnitude)
|
||||
lat_ib_ns = int(LATENCY_INFINIBAND.to(NS).magnitude)
|
||||
lat_ssd_ns = int(LATENCY_NVME_SSD.to(NS).magnitude)
|
||||
|
||||
bw_pcie5 = int(Hardware.Cloud.H100.interconnect.bandwidth.m_as(GB/second))
|
||||
bw_dram = int(Hardware.Tech.Storage.SystemMemory.bandwidth.m_as(GB/second))
|
||||
bw_ib = int(f.InfiniBand_NDR.bandwidth.m_as(GB/second))
|
||||
bw_pcie5 = int(Hardware.Cloud.H100.interconnect.bandwidth.to(GB/second).magnitude)
|
||||
bw_dram = int(Hardware.Tech.Storage.SystemMemory.bandwidth.to(GB/second).magnitude)
|
||||
bw_ib = int(f.InfiniBand_NDR.bandwidth.to(GB/second).magnitude)
|
||||
|
||||
flops_h100_fp16 = int(h_h100.compute.peak_flops.m_as(TFLOPs/second))
|
||||
flops_h100_fp8 = int(h_h100.compute.precision_flops["fp8"].m_as(TFLOPs/second))
|
||||
flops_a100_fp16 = int(h_a100.compute.peak_flops.m_as(TFLOPs/second))
|
||||
mobile_tops_int8 = int(Hardware.Mobile.iPhone15Pro.compute.peak_flops.m_as(TOPS))
|
||||
flops_h100_fp16 = int(h_h100.compute.peak_flops.to(TFLOPs/second).magnitude)
|
||||
flops_h100_fp8 = int(h_h100.compute.precision_flops["fp8"].to(TFLOPs/second).magnitude)
|
||||
flops_a100_fp16 = int(h_a100.compute.peak_flops.to(TFLOPs/second).magnitude)
|
||||
mobile_tops_int8 = int(Hardware.Mobile.iPhone15Pro.compute.peak_flops.to(TOPS).magnitude)
|
||||
dc_mobile_ratio = flops_h100_fp16 / mobile_tops_int8
|
||||
|
||||
ridge_a100 = int(h_a100.ridge_point().m_as("flop/byte"))
|
||||
ridge_h100 = int(h_h100.ridge_point().m_as("flop/byte"))
|
||||
ridge_a100 = int(h_a100.ridge_point().to(flop/byte).magnitude)
|
||||
ridge_h100 = int(h_h100.ridge_point().to(flop/byte).magnitude)
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
# Canonical _str forms for prose; raw ints/floats above kept for arithmetic.
|
||||
@@ -149,11 +149,11 @@ class NumbersToKnow:
|
||||
lat_ib_ns_str = fmt(lat_ib_ns, precision=0, commas=False)
|
||||
lat_ssd_ns_str = fmt(lat_ssd_ns, precision=0, commas=False)
|
||||
|
||||
bw_hbm_h100_str = fmt(h_h100.memory.bandwidth.m_as(TB/second), precision=1, commas=False)
|
||||
bw_hbm_h100_str = fmt(h_h100.memory.bandwidth.to(TB/second).magnitude, precision=1, commas=False)
|
||||
bw_pcie5_str = fmt(bw_pcie5, precision=0, commas=False)
|
||||
bw_dram_str = fmt(bw_dram, precision=0, commas=False)
|
||||
bw_ib_str = fmt(bw_ib, precision=0, commas=False)
|
||||
bw_nvme_str = fmt(Hardware.Tech.Storage.NvmeGen4.bandwidth.m_as(GB/second), precision=0, commas=False)
|
||||
bw_nvme_str = fmt(Hardware.Tech.Storage.NvmeGen4.bandwidth.to(GB/second).magnitude, precision=0, commas=False)
|
||||
|
||||
flops_h100_fp16_str = fmt(flops_h100_fp16, precision=0, commas=False)
|
||||
flops_h100_fp8_str = fmt(flops_h100_fp8, precision=0, commas=False)
|
||||
@@ -379,8 +379,8 @@ class A100RooflineExample:
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
a100_fp16 = Hardware.Cloud.A100.compute.peak_flops
|
||||
a100_bw = Hardware.Cloud.A100.memory.bandwidth
|
||||
a100_fp16_raw_value = a100_fp16.m_as(TFLOPs/second)
|
||||
a100_bw_raw_value = a100_bw.m_as(TB/second)
|
||||
a100_fp16_raw_value = a100_fp16.to(TFLOPs/second).magnitude
|
||||
a100_bw_raw_value = a100_bw.to(TB/second).magnitude
|
||||
ridge_point_value = int(a100_fp16_raw_value / a100_bw_raw_value)
|
||||
gemm_intensity_value = int(n_gemm_value / 3)
|
||||
relu_achieved_tflops_value = relu_intensity_value * a100_bw_raw_value
|
||||
@@ -652,7 +652,7 @@ class TrainingTimeRef:
|
||||
d_tokens = Literature.Chinchilla.TokensPerParam * p_params
|
||||
n_gpus = 1
|
||||
x_flops_q = Hardware.Cloud.A100.compute.peak_flops
|
||||
x_flops = x_flops_q.m_as(TFLOPs/second)
|
||||
x_flops = x_flops_q.to(TFLOPs/second).magnitude
|
||||
u_mfu = calibration.REFERENCE_MFU_SUSTAINED
|
||||
|
||||
# Rename variables for internal logic
|
||||
@@ -663,7 +663,7 @@ class TrainingTimeRef:
|
||||
u_mfu_value = u_mfu
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
total_flops = calc_transformer_training_flops(p_params_value, d_tokens_value).m_as(ureg.flop)
|
||||
total_flops = calc_transformer_training_flops(p_params_value, d_tokens_value).to(flop).magnitude
|
||||
# Step 1: x_flops_value is in TFLOP/s (1e12)
|
||||
throughput = n_gpus_value * (x_flops_value) * u_mfu_value
|
||||
|
||||
@@ -761,9 +761,9 @@ class LittlesLawExample:
|
||||
lambda_qps_value = 1000 # queries per second
|
||||
w_latency_s_value = 0.050 # 50 ms in seconds
|
||||
mem_per_req = 1 * GB
|
||||
mem_per_req_gb_value = mem_per_req.m_as(GB)
|
||||
mem_per_req_gb_value = mem_per_req.to(GB).magnitude
|
||||
gpu_mem = 24 * GB
|
||||
gpu_mem_gb_value = gpu_mem.m_as(GB)
|
||||
gpu_mem_gb_value = gpu_mem.to(GB).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
l_concurrent_value = lambda_qps_value * w_latency_s_value
|
||||
@@ -830,34 +830,34 @@ class ArchReferenceTables:
|
||||
LATENCY_NVLINK = Hardware.Tech.Interconnect.NVLink.latency
|
||||
LATENCY_PCIE_GEN5 = Hardware.Tech.Interconnect.PCIeGen5.latency
|
||||
LATENCY_NVME_SSD = Hardware.Tech.Storage.NvmeGen4.latency
|
||||
LATENCY_INFINIBAND = round(Systems.Fabrics.InfiniBand_NDR.latency.m_as(NS)) * NS # µs→ns, round to exact 5000
|
||||
LATENCY_INFINIBAND = round(Systems.Fabrics.InfiniBand_NDR.latency.to(NS).magnitude) * NS # µs→ns, round to exact 5000
|
||||
h100_l2 = Hardware.Cloud.H100.memory.l2_cache
|
||||
tpuv5_l2 = Hardware.Cloud.TPUv5p.memory.l2_cache
|
||||
register_ns_value = LATENCY_REGISTER_REF.m_as(NS)
|
||||
register_ns_value = LATENCY_REGISTER_REF.to(NS).magnitude
|
||||
analogy_base_seconds = 10
|
||||
analogy_seconds_per_ns = analogy_base_seconds / register_ns_value
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
dram_pj_value = int(ENERGY_DRAM_ACCESS_PJ.m_as(ureg.picojoule))
|
||||
flop_pj_value = ENERGY_FLOP_FP16_PJ.m_as(ureg.picojoule)
|
||||
dram_pj_value = int(ENERGY_DRAM_ACCESS_PJ.to(pJ).magnitude)
|
||||
flop_pj_value = ENERGY_FLOP_FP16_PJ.to(pJ).magnitude
|
||||
energy_ratio_value = int(dram_pj_value / flop_pj_value)
|
||||
|
||||
l1_ns_value = int(LATENCY_L1_REGISTER.m_as(NS))
|
||||
l2_ns_value = int(LATENCY_L2_CACHE.m_as(NS))
|
||||
hbm_ns_value = int(LATENCY_HBM3.m_as(NS))
|
||||
nvlink_ns_value = int(LATENCY_NVLINK.m_as(NS))
|
||||
pcie_ns_value = int(LATENCY_PCIE_GEN5.m_as(NS))
|
||||
ib_ns_value = int(LATENCY_INFINIBAND.m_as(NS))
|
||||
ssd_ns_value = int(LATENCY_NVME_SSD.m_as(NS))
|
||||
l1_ns_value = int(LATENCY_L1_REGISTER.to(NS).magnitude)
|
||||
l2_ns_value = int(LATENCY_L2_CACHE.to(NS).magnitude)
|
||||
hbm_ns_value = int(LATENCY_HBM3.to(NS).magnitude)
|
||||
nvlink_ns_value = int(LATENCY_NVLINK.to(NS).magnitude)
|
||||
pcie_ns_value = int(LATENCY_PCIE_GEN5.to(NS).magnitude)
|
||||
ib_ns_value = int(LATENCY_INFINIBAND.to(NS).magnitude)
|
||||
ssd_ns_value = int(LATENCY_NVME_SSD.to(NS).magnitude)
|
||||
|
||||
h100_flops = Hardware.Cloud.H100.compute.peak_flops
|
||||
h100_bw = Hardware.Cloud.H100.memory.bandwidth
|
||||
h100_cap = int(Hardware.Cloud.H100.memory.capacity.m_as(GB)) * GB
|
||||
h100_cap = int(Hardware.Cloud.H100.memory.capacity.to(GB).magnitude) * GB
|
||||
h100_nvlink = Hardware.Cloud.H100.nvlink.bandwidth
|
||||
|
||||
tpuv5_flops = Hardware.Cloud.TPUv5p.compute.peak_flops
|
||||
tpuv5_bw = Hardware.Cloud.TPUv5p.memory.bandwidth
|
||||
tpuv5_cap = int(Hardware.Cloud.TPUv5p.memory.capacity.m_as(GB)) * GB
|
||||
tpuv5_cap = int(Hardware.Cloud.TPUv5p.memory.capacity.to(GB).magnitude) * GB
|
||||
tpuv5_ici = Hardware.Cloud.TPUv5p.nvlink.bandwidth
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -902,17 +902,17 @@ class ArchReferenceTables:
|
||||
tpuv5_ici_str = fmt_qty(tpuv5_ici, GB/second, precision=0, commas=False, unit_label="GB")
|
||||
tpuv5_l2_mb_str = fmt_qty(tpuv5_l2, MB, precision=0, commas=False)
|
||||
|
||||
bw_hbm_str = fmt(Hardware.Cloud.H100.memory.bandwidth.m_as(GB/second), precision=0)
|
||||
bw_nvlink_str = fmt(Hardware.Cloud.H100.nvlink.bandwidth.m_as(GB/second), precision=0)
|
||||
bw_pcie_str = fmt(Hardware.Cloud.H100.interconnect.bandwidth.m_as(GB/second), precision=0)
|
||||
bw_dram_str = fmt(Hardware.Tech.Storage.SystemMemory.bandwidth.m_as(GB/second), precision=0)
|
||||
bw_ssd_str = fmt(Hardware.Tech.Storage.NvmeGen4.bandwidth.m_as(GB/second), precision=0, commas=False)
|
||||
bw_net_str = fmt(Systems.Fabrics.InfiniBand_NDR.bandwidth.m_as(GB/second), precision=0, commas=False)
|
||||
bw_hbm_str = fmt(Hardware.Cloud.H100.memory.bandwidth.to(GB/second).magnitude, precision=0)
|
||||
bw_nvlink_str = fmt(Hardware.Cloud.H100.nvlink.bandwidth.to(GB/second).magnitude, precision=0)
|
||||
bw_pcie_str = fmt(Hardware.Cloud.H100.interconnect.bandwidth.to(GB/second).magnitude, precision=0)
|
||||
bw_dram_str = fmt(Hardware.Tech.Storage.SystemMemory.bandwidth.to(GB/second).magnitude, precision=0)
|
||||
bw_ssd_str = fmt(Hardware.Tech.Storage.NvmeGen4.bandwidth.to(GB/second).magnitude, precision=0, commas=False)
|
||||
bw_net_str = fmt(Systems.Fabrics.InfiniBand_NDR.bandwidth.to(GB/second).magnitude, precision=0, commas=False)
|
||||
|
||||
e_reg_str = MarkdownStr(f"{ENERGY_REG_PJ.m_as(ureg.picojoule):.2f}")
|
||||
e_l1_str = MarkdownStr(f"{ENERGY_SRAM_L1_PJ.m_as(ureg.picojoule):.1f}")
|
||||
e_l2_str = MarkdownStr(f"{ENERGY_SRAM_L2_PJ.m_as(ureg.picojoule):.1f}")
|
||||
e_dram_str = MarkdownStr(f"{ENERGY_DRAM_ACCESS_PJ.m_as(ureg.picojoule):.0f}")
|
||||
e_reg_str = MarkdownStr(f"{ENERGY_REG_PJ.to(pJ).magnitude:.2f}")
|
||||
e_l1_str = MarkdownStr(f"{ENERGY_SRAM_L1_PJ.to(pJ).magnitude:.1f}")
|
||||
e_l2_str = MarkdownStr(f"{ENERGY_SRAM_L2_PJ.to(pJ).magnitude:.1f}")
|
||||
e_dram_str = MarkdownStr(f"{ENERGY_DRAM_ACCESS_PJ.to(pJ).magnitude:.0f}")
|
||||
e_ssd_str = MarkdownStr("~5,000")
|
||||
e_net_str = MarkdownStr("~10,000")
|
||||
```
|
||||
@@ -1065,10 +1065,10 @@ class BandwidthLatencySetup:
|
||||
"""Namespace for bandwidth-latency lead-in constants."""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
bw_gbps_value = Systems.Fabrics.Ethernet_10G.bandwidth.m_as(Gbps)
|
||||
bw_gbps_value = Systems.Fabrics.Ethernet_10G.bandwidth.to(Gbps).magnitude
|
||||
ping_ms_value = 10 # Network latency in ms
|
||||
data = 1 * KB # Packet size
|
||||
data_kb_value = data.m_as(KB)
|
||||
data_kb_value = data.to(KB).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
# No derived values—only string formatting.
|
||||
@@ -1108,8 +1108,8 @@ class BandwidthLatencyExample:
|
||||
"""Namespace for latency-bound vs. bandwidth-bound transmission time example."""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
data_kb_value = (1 * KB).m_as(byte)
|
||||
bw_gbps_value = Systems.Fabrics.Ethernet_10G.bandwidth.m_as(Gbps) * BILLION
|
||||
data_kb_value = (1 * KB).to(byte).magnitude
|
||||
bw_gbps_value = Systems.Fabrics.Ethernet_10G.bandwidth.to(Gbps).magnitude * BILLION
|
||||
large_data_gb_value = 1
|
||||
ping_ms_value = 10
|
||||
|
||||
@@ -1117,7 +1117,7 @@ class BandwidthLatencyExample:
|
||||
tx_time_s_value = (data_kb_value * BITS_PER_BYTE) / bw_gbps_value
|
||||
tx_time_us_value = tx_time_s_value * MILLION
|
||||
|
||||
large_data_bits_value = large_data_gb_value * (1 * GB).m_as(byte) * BITS_PER_BYTE
|
||||
large_data_bits_value = large_data_gb_value * (1 * GB).to(byte).magnitude * BITS_PER_BYTE
|
||||
large_tx_time_s_value = large_data_bits_value/bw_gbps_value
|
||||
total_large_time_ms_value = ping_ms_value + large_tx_time_s_value * THOUSAND
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ from mlsysim.core.constants import TFLOPs, second
|
||||
from mlsysim.fmt import fmt, fmt_qty
|
||||
|
||||
class A100PeakBenchmark:
|
||||
_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs / second)
|
||||
_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs / second).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
a100_tflops_fp16_str = fmt_qty(_peak_tflops * TFLOP / second, TFLOP / second, precision=0, commas=False)
|
||||
@@ -114,8 +114,8 @@ class MobileNetLighthouseSetup:
|
||||
_mobilenet_v2 = Models.Vision.MobileNetV2
|
||||
_edgetpu_latency_ms = 2
|
||||
_cpu_latency_ms = 15
|
||||
_mv2_size_mb = _mobilenet_v2.size_in_bytes(BYTES_FP32).m_as(MB)
|
||||
_mv2_int8_mb = _mobilenet_v2.size_in_bytes(BYTES_INT8).m_as(MB)
|
||||
_mv2_size_mb = _mobilenet_v2.size_in_bytes(BYTES_FP32).to(MB).magnitude
|
||||
_mv2_int8_mb = _mobilenet_v2.size_in_bytes(BYTES_INT8).to(MB).magnitude
|
||||
_mv2_compress = _mv2_size_mb / _mv2_int8_mb
|
||||
|
||||
check(_mv2_compress == 4.0, f"Expected 4x INT8 compression, got {_mv2_compress:.1f}x")
|
||||
@@ -275,8 +275,8 @@ from mlsysim.fmt import fmt, fmt_qty
|
||||
|
||||
class MobileNetEnergyFlops:
|
||||
_flops_ratio = (
|
||||
Models.Vision.ResNet50.inference_flops.m_as(GFLOPs)
|
||||
/ Models.Vision.MobileNetV2.inference_flops.m_as(GFLOPs)
|
||||
Models.Vision.ResNet50.inference_flops.to(GFLOPs).magnitude
|
||||
/ Models.Vision.MobileNetV2.inference_flops.to(GFLOPs).magnitude
|
||||
)
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -602,12 +602,12 @@ System benchmarks serve two functions. For practitioners, they enable informed h
|
||||
class A100Roofline:
|
||||
"""A100 hardware constants for roofline analysis."""
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
_a100_fp16 = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
_a100_fp32 = Hardware.Cloud.A100.compute.precision_flops["fp32"].m_as(TFLOPs/second)
|
||||
_a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
_a100_fp16 = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
_a100_fp32 = Hardware.Cloud.A100.compute.precision_flops["fp32"].to(TFLOPs/second).magnitude
|
||||
_a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
_a100_ridge = Hardware.Cloud.A100.ridge_point().m_as('flop/byte')
|
||||
_a100_ridge = Hardware.Cloud.A100.ridge_point().to(flop/byte).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
a100_tflops_fp16_str = fmt_qty(_a100_fp16 * TFLOP / second, TFLOP / second, precision=0, commas=False)
|
||||
@@ -661,8 +661,8 @@ class RooflineExamples:
|
||||
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
# A100 Specs (re-derived locally for safety)
|
||||
peak_flops = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
peak_bw = Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
peak_flops = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
peak_bw = Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
ridge_point = peak_flops / peak_bw # ~153
|
||||
|
||||
# ResNet (Compute Bound)
|
||||
@@ -672,8 +672,8 @@ class RooflineExamples:
|
||||
|
||||
# BERT (Memory Bound at Batch=1)
|
||||
bert_model = Models.Language.BERT_Base
|
||||
bert_flops_b = bert_model.inference_flops.m_as(Bparam)
|
||||
bert_weight_mb = bert_model.size_in_bytes(BYTES_FP32).m_as(MB)
|
||||
bert_flops_b = bert_model.inference_flops.to(Bparam).magnitude
|
||||
bert_weight_mb = bert_model.size_in_bytes(BYTES_FP32).to(MB).magnitude
|
||||
bert_util_peak = 0.85
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
@@ -779,25 +779,25 @@ class BertRoofline:
|
||||
p_b1 = Engine.solve(m_bert, h_a100, batch_size=batch_1, precision="fp32", efficiency=1.0)
|
||||
p_b32 = Engine.solve(m_bert, h_a100, batch_size=batch_32, precision="fp32", efficiency=0.85)
|
||||
|
||||
bert_params_m = m_bert.parameters.m_as(Mparam)
|
||||
bert_flops_b = m_bert.inference_flops.m_as(Bparam)
|
||||
bert_weight_mb = p_b1.memory_footprint.m_as(MB)
|
||||
bert_params_m = m_bert.parameters.to(Mparam).magnitude
|
||||
bert_flops_b = m_bert.inference_flops.to(Bparam).magnitude
|
||||
bert_weight_mb = p_b1.memory_footprint.to(MB).magnitude
|
||||
|
||||
ai_b1 = (m_bert.inference_flops / p_b1.memory_footprint).m_as("flop/byte")
|
||||
ai_b1 = (m_bert.inference_flops / p_b1.memory_footprint).to(flop/byte).magnitude
|
||||
# Realized TFLOP/s = throughput * flops_per_inference
|
||||
perf_b1 = (p_b1.throughput * m_bert.inference_flops).m_as(TFLOPs/second)
|
||||
perf_b1 = (p_b1.throughput * m_bert.inference_flops).to(TFLOPs/second).magnitude
|
||||
util_b1 = p_b1.mfu * 100.0
|
||||
|
||||
flops_b32 = bert_flops_b * batch_32
|
||||
ai_b32 = (m_bert.inference_flops * batch_32 / p_b1.memory_footprint).m_as("flop/byte")
|
||||
ai_b32 = (m_bert.inference_flops * batch_32 / p_b1.memory_footprint).to(flop/byte).magnitude
|
||||
|
||||
# Is it compute bound now?
|
||||
is_compute_bound_b32 = p_b32.bottleneck == "Compute"
|
||||
perf_b32 = (p_b32.throughput * m_bert.inference_flops).m_as(TFLOPs/second)
|
||||
perf_b32 = (p_b32.throughput * m_bert.inference_flops).to(TFLOPs/second).magnitude
|
||||
|
||||
peak_flops = h_a100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
peak_bw = h_a100.memory.bandwidth.m_as(TB/second)
|
||||
ridge_point = h_a100.ridge_point().m_as("flop/byte")
|
||||
peak_flops = h_a100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
peak_bw = h_a100.memory.bandwidth.to(TB/second).magnitude
|
||||
ridge_point = h_a100.ridge_point().to(flop/byte).magnitude
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
check(ai_b32 > ai_b1, "Batching must increase Arithmetic Intensity.")
|
||||
@@ -1126,8 +1126,8 @@ from mlsysim.fmt import fmt
|
||||
|
||||
class H100SolCheck:
|
||||
"""H100 peak throughput for micro-benchmark SOL checks."""
|
||||
_h100_fp16 = Hardware.Cloud.H100.compute.peak_flops.m_as(TFLOPs / second)
|
||||
_h100_fp8 = Hardware.Cloud.H100.compute.precision_flops["fp8"].m_as(TFLOPs / second)
|
||||
_h100_fp16 = Hardware.Cloud.H100.compute.peak_flops.to(TFLOPs / second).magnitude
|
||||
_h100_fp8 = Hardware.Cloud.H100.compute.precision_flops["fp8"].to(TFLOPs / second).magnitude
|
||||
sol_example_tflops_value = 10
|
||||
cache_bw_low_tbs_value = 5
|
||||
cache_bw_high_tbs_value = 10
|
||||
@@ -1522,7 +1522,7 @@ Dataset selection shapes everything downstream. In the audio anomaly detection e
|
||||
|
||||
class BertModelSpecs:
|
||||
"""BERT-Large parameter count for the model selection footnote."""
|
||||
_bert_large_m = Models.Language.BERT_Large.parameters.m_as(Mparam)
|
||||
_bert_large_m = Models.Language.BERT_Large.parameters.to(Mparam).magnitude
|
||||
bert_large_params_m_str = fmt(_bert_large_m, precision=0, commas=False)
|
||||
```
|
||||
|
||||
@@ -1616,10 +1616,10 @@ class AnomalySpecs:
|
||||
"""Anomaly detection model constants for benchmark component examples."""
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
from mlsysim import Scenarios
|
||||
_anomaly_k = Models.Tiny.AnomalyDetector.parameters.m_as(Kparam)
|
||||
_anomaly_latency_ms = Scenarios.AnomalyModel.Latency.m_as(ureg.ms)
|
||||
_anomaly_k = Models.Tiny.AnomalyDetector.parameters.to(Kparam).magnitude
|
||||
_anomaly_latency_ms = Scenarios.AnomalyModel.Latency.to(ms).magnitude
|
||||
_anomaly_auc = Scenarios.AnomalyModel.Auc
|
||||
_anomaly_energy_uj = Scenarios.AnomalyModel.Energy.m_as(ureg.microjoule)
|
||||
_anomaly_energy_uj = Scenarios.AnomalyModel.Energy.to(ureg.microjoule).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
anomaly_params_k_str = fmt_count(_anomaly_k * THOUSAND, scale='K', precision=0, commas=False)
|
||||
@@ -1705,8 +1705,8 @@ class MobileNetTradeoffCalc:
|
||||
latency_int8_ms = 35
|
||||
acc_fp32 = 71.8
|
||||
acc_int8 = 70.9
|
||||
size_fp32_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_FP32).m_as(MB)
|
||||
size_int8_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_INT8).m_as(MB)
|
||||
size_fp32_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_FP32).to(MB).magnitude
|
||||
size_int8_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_INT8).to(MB).magnitude
|
||||
milliseconds_per_second = 1000
|
||||
|
||||
# EXECUTE
|
||||
@@ -1829,8 +1829,8 @@ from mlsysim.fmt import fmt_count
|
||||
class CompressionModelSpecs:
|
||||
"""MobileNetV2 vs ResNet-50 parameter counts for compression benchmarks."""
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
_mobilenet_m = Models.Vision.MobileNetV2.parameters.m_as(Mparam)
|
||||
_resnet50_m = Models.Vision.ResNet50.parameters.m_as(Mparam)
|
||||
_mobilenet_m = Models.Vision.MobileNetV2.parameters.to(Mparam).magnitude
|
||||
_resnet50_m = Models.Vision.ResNet50.parameters.to(Mparam).magnitude
|
||||
_mobilenet_top1 = 72
|
||||
_resnet50_top1 = 76
|
||||
|
||||
@@ -1970,16 +1970,16 @@ from mlsysim.core.units import MWh
|
||||
class InferenceEnergy:
|
||||
"""Per-query energy calculation for two inference latency scenarios."""
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
_power_w = Hardware.Cloud.V100.tdp.m_as(watt) # Typical accelerator TDP (watts)
|
||||
_power_w = Hardware.Cloud.V100.tdp.to(watt).magnitude # Typical accelerator TDP (watts)
|
||||
_latency_fast_ms = 10 # Fast inference (ms)
|
||||
_latency_slow_ms = 100 # Slow inference (ms)
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
_latency_fast_s = (_latency_fast_ms * ms).m_as(second) # seconds
|
||||
_latency_fast_s = (_latency_fast_ms * ms).to(second).magnitude # seconds
|
||||
_energy_fast_j = _power_w * _latency_fast_s # joules
|
||||
_energy_fast_wh = _energy_fast_j / (1 * hour).m_as(second) # watt-hours
|
||||
_energy_slow_j = _power_w * (_latency_slow_ms * ms).m_as(second)
|
||||
_energy_slow_wh = _energy_slow_j / (1 * hour).m_as(second)
|
||||
_energy_fast_wh = _energy_fast_j / (1 * hour).to(second).magnitude # watt-hours
|
||||
_energy_slow_j = _power_w * (_latency_slow_ms * ms).to(second).magnitude
|
||||
_energy_slow_wh = _energy_slow_j / (1 * hour).to(second).magnitude
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
check(_energy_fast_j == 3.0, f"Expected 3 J at 300 W for 10 ms, got {_energy_fast_j:.1f}")
|
||||
@@ -2040,7 +2040,7 @@ These benchmarks are vital because training represents the largest capital expen
|
||||
class GPT3TrainingSpecs:
|
||||
"""GPT-3 parameter and token counts for training benchmark context."""
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
_params_b = Models.Language.GPT3.parameters.m_as(Bparam)
|
||||
_params_b = Models.Language.GPT3.parameters.to(Bparam).magnitude
|
||||
_tokens_b = Models.Language.GPT3.training_tokens.to('count').magnitude / BILLION
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -2452,11 +2452,11 @@ class EnergyPerOp:
|
||||
# FP constants use pJ/FLOP; INT8 uses pJ/op. Use .magnitude for bare numeric values.
|
||||
_fp32_pj = ENERGY_FLOP_FP32_PJ.magnitude
|
||||
_fp16_pj = ENERGY_FLOP_FP16_PJ.magnitude
|
||||
_int8_pj = ENERGY_OP_INT8_PJ.m_as(ureg.picojoule/ureg.count)
|
||||
_int8_pj = ENERGY_OP_INT8_PJ.to(pJ).magnitude
|
||||
_reg_pj = ENERGY_REG_PJ
|
||||
_l1_pj = ENERGY_SRAM_L1_PJ
|
||||
_l2_pj = ENERGY_SRAM_L2_PJ
|
||||
_dram_pj = ENERGY_DRAM_PJ_PER_BYTE.m_as(ureg.picojoule/ureg.byte)
|
||||
_dram_pj = ENERGY_DRAM_PJ_PER_BYTE.to(pJ / byte).magnitude
|
||||
dram_vs_reg_ratio_value = round(_dram_pj / _reg_pj / 1000) * THOUSAND
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -2484,30 +2484,30 @@ from mlsysim.fmt import fmt, check
|
||||
class EnergyBreakdownCalc:
|
||||
"""FP32 vs INT8 MobileNet inference energy: memory load dominates; INT8 attacks both sources."""
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
m_params = Models.Vision.MobileNetV2.parameters.m_as(Mparam)
|
||||
m_ops = Models.Vision.MobileNetV2.inference_flops.m_as(MFLOPs)
|
||||
m_params = Models.Vision.MobileNetV2.parameters.to(Mparam).magnitude
|
||||
m_ops = Models.Vision.MobileNetV2.inference_flops.to(MFLOPs).magnitude
|
||||
ENERGY_DRAM_PJ_PER_BYTE = Hardware.Tech.Memory.DRAM.energy_per_byte
|
||||
ENERGY_FLOP_FP32_PJ = Hardware.Tech.Op.FlopFp32.energy
|
||||
ENERGY_OP_INT8_PJ = Hardware.Tech.Op.OpInt8.energy
|
||||
_dram_pj = ENERGY_DRAM_PJ_PER_BYTE.m_as(ureg.picojoule/byte)
|
||||
_fp32_pj = ENERGY_FLOP_FP32_PJ.m_as(ureg.picojoule/ureg.flop)
|
||||
_int8_pj = ENERGY_OP_INT8_PJ.m_as(ureg.picojoule/ureg.count)
|
||||
_bytes_fp32 = BYTES_FP32.m_as(byte)
|
||||
_bytes_int8 = BYTES_INT8.m_as(byte)
|
||||
_dram_pj = ENERGY_DRAM_PJ_PER_BYTE.to(pJ / byte).magnitude
|
||||
_fp32_pj = ENERGY_FLOP_FP32_PJ.to(pJ / flop).magnitude
|
||||
_int8_pj = ENERGY_OP_INT8_PJ.to(pJ).magnitude
|
||||
_bytes_fp32 = BYTES_FP32.to(byte).magnitude
|
||||
_bytes_int8 = BYTES_INT8.to(byte).magnitude
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
e_fp32_load = (m_params * MILLION * _bytes_fp32 * _dram_pj) / MILLION
|
||||
e_fp32_compute = (m_ops * MILLION * _fp32_pj) / MILLION
|
||||
e_int8_load = (m_params * MILLION * _bytes_int8 * _dram_pj) / MILLION
|
||||
e_int8_compute = (m_ops * MILLION * _int8_pj) / MILLION
|
||||
m_fp32_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_FP32).m_as(MB)
|
||||
m_int8_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_INT8).m_as(MB)
|
||||
m_fp32_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_FP32).to(MB).magnitude
|
||||
m_int8_mb = Models.Vision.MobileNetV2.size_in_bytes(BYTES_INT8).to(MB).magnitude
|
||||
e_fp32_total = e_fp32_load + e_fp32_compute
|
||||
e_int8_total = e_int8_load + e_int8_compute
|
||||
s_load = e_fp32_load/e_int8_load
|
||||
s_compute = e_fp32_compute/e_int8_compute
|
||||
s_total = e_fp32_total/e_int8_total
|
||||
e_fp32_load_mj = (e_fp32_load * ureg.microjoule).m_as(ureg.millijoule)
|
||||
e_fp32_compute_mj = (e_fp32_compute * ureg.microjoule).m_as(ureg.millijoule)
|
||||
e_fp32_load_mj = (e_fp32_load * ureg.microjoule).to(mJ).magnitude
|
||||
e_fp32_compute_mj = (e_fp32_compute * ureg.microjoule).to(mJ).magnitude
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
check(round(m_fp32_mb) == 14, f"MobileNetV2 FP32 size should be about 14 MB, got {m_fp32_mb:.1f}")
|
||||
check(s_load == 4.0, f"Expected 4x load-energy reduction, got {s_load:.1f}x")
|
||||
@@ -3069,7 +3069,7 @@ from mlsysim.fmt import fmt, fmt_qty, check, fmt_count
|
||||
class ColdStartTransferCalc:
|
||||
"""FP16 weight transfer lower bound for a 7-billion-parameter model on PCIe 4.0."""
|
||||
# LOAD
|
||||
params_b = Models.Language.Llama2_7B.parameters.m_as(Bparam)
|
||||
params_b = Models.Language.Llama2_7B.parameters.to(Bparam).magnitude
|
||||
bytes_per_param = 2
|
||||
pcie_effective_gbs = 25
|
||||
|
||||
@@ -3117,7 +3117,7 @@ class TopsFootnoteAnchor:
|
||||
"""Local H100 INT8 TOPS anchor for the metric-comparability footnote."""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
h100_tops_int8 = Hardware.Cloud.H100.compute.precision_flops["int8"].m_as(TOPS)
|
||||
h100_tops_int8 = Hardware.Cloud.H100.compute.precision_flops["int8"].to(TOPS).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
# Direct hardware-constant lookup.
|
||||
@@ -4514,9 +4514,9 @@ class MobileNetINT8Calc:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
_mobilenet_v2 = Models.Vision.MobileNetV2
|
||||
acc_fp32 = 71.8
|
||||
params_m = _mobilenet_v2.parameters.m_as(Mparam)
|
||||
size_fp32_mb = _mobilenet_v2.size_in_bytes(BYTES_FP32).m_as(MB)
|
||||
size_int8_mb = _mobilenet_v2.size_in_bytes(BYTES_INT8).m_as(MB)
|
||||
params_m = _mobilenet_v2.parameters.to(Mparam).magnitude
|
||||
size_fp32_mb = _mobilenet_v2.size_in_bytes(BYTES_FP32).to(MB).magnitude
|
||||
size_int8_mb = _mobilenet_v2.size_in_bytes(BYTES_INT8).to(MB).magnitude
|
||||
acc_int8 = 70.9
|
||||
top5_fp32 = 91.0
|
||||
top5_int8 = 90.4
|
||||
@@ -5254,14 +5254,14 @@ class PrecisionEnergySummary:
|
||||
"""Local summary ratio for MobileNetV2 FP32 vs. INT8 inference energy."""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
m_params = Models.Vision.MobileNetV2.parameters.m_as(Mparam)
|
||||
m_ops = Models.Vision.MobileNetV2.inference_flops.m_as(MFLOPs)
|
||||
m_params = Models.Vision.MobileNetV2.parameters.to(Mparam).magnitude
|
||||
m_ops = Models.Vision.MobileNetV2.inference_flops.to(MFLOPs).magnitude
|
||||
ENERGY_DRAM_PJ_PER_BYTE = Hardware.Tech.Memory.DRAM.energy_per_byte
|
||||
ENERGY_FLOP_FP32_PJ = Hardware.Tech.Op.FlopFp32.energy
|
||||
ENERGY_OP_INT8_PJ = Hardware.Tech.Op.OpInt8.energy
|
||||
dram_pj = ENERGY_DRAM_PJ_PER_BYTE.m_as(ureg.picojoule/byte)
|
||||
fp32_pj = ENERGY_FLOP_FP32_PJ.m_as(ureg.picojoule/ureg.flop)
|
||||
int8_pj = ENERGY_OP_INT8_PJ.m_as(ureg.picojoule/ureg.count)
|
||||
dram_pj = ENERGY_DRAM_PJ_PER_BYTE.to(pJ / byte).magnitude
|
||||
fp32_pj = ENERGY_FLOP_FP32_PJ.to(pJ / flop).magnitude
|
||||
int8_pj = ENERGY_OP_INT8_PJ.to(pJ).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
e_fp32_load = (m_params * MILLION * 4 * dram_pj) / MILLION
|
||||
|
||||
@@ -152,14 +152,14 @@ class MobileNetJourneyMath:
|
||||
|
||||
# GUARD
|
||||
check(8 <= standard_to_depthwise <= 9, "Depthwise separable example should land near 8--9x.")
|
||||
check(13 <= resnet_to_mobilenet.m_as("") <= 15, "ResNet-50/MobileNetV2 ratio drifted.")
|
||||
check(int8_vs_fp32.m_as("") == 4 and int8_vs_fp16.m_as("") == 2, "INT8 byte ratios changed.")
|
||||
check(13 <= resnet_to_mobilenet.to("").magnitude <= 15, "ResNet-50/MobileNetV2 ratio drifted.")
|
||||
check(int8_vs_fp32.to("").magnitude == 4 and int8_vs_fp16.to("").magnitude == 2, "INT8 byte ratios changed.")
|
||||
|
||||
# OUTPUT
|
||||
depthwise_reduction_str = fmt_multiple(standard_to_depthwise, precision=1, commas=False)
|
||||
resnet_ops_reduction_str = fmt_multiple(resnet_to_mobilenet.m_as(""), precision=1, commas=False)
|
||||
int8_fp32_reduction_str = fmt_multiple(int8_vs_fp32.m_as(""), precision=0, commas=False)
|
||||
int8_fp16_reduction_str = fmt_multiple(int8_vs_fp16.m_as(""), precision=0, commas=False)
|
||||
resnet_ops_reduction_str = fmt_multiple(resnet_to_mobilenet.to("").magnitude, precision=1, commas=False)
|
||||
int8_fp32_reduction_str = fmt_multiple(int8_vs_fp32.to("").magnitude, precision=0, commas=False)
|
||||
int8_fp16_reduction_str = fmt_multiple(int8_vs_fp16.to("").magnitude, precision=0, commas=False)
|
||||
```
|
||||
|
||||
@Tbl-lighthouse-journey-mobilenet traces this journey for a single model, MobileNetV2, demonstrating how every chapter's principles converge on a single engineering artifact. The table walks through seven phases (from foundational constraints through architecture, training, compression, acceleration, serving, and operations) showing how each phase's decisions propagate forward to shape what becomes possible in subsequent phases.
|
||||
@@ -223,12 +223,12 @@ class EnergyMovementMath:
|
||||
dram_vs_fp16 = dram_access / fp16_flop
|
||||
|
||||
# GUARD
|
||||
check(100 <= dram_vs_fp32.m_as("") <= 1000, "DRAM/FP32 energy ratio left expected range.")
|
||||
check(100 <= dram_vs_fp16.m_as("") <= 1000, "DRAM/FP16 energy ratio left expected range.")
|
||||
check(100 <= dram_vs_fp32.to("").magnitude <= 1000, "DRAM/FP32 energy ratio left expected range.")
|
||||
check(100 <= dram_vs_fp16.to("").magnitude <= 1000, "DRAM/FP16 energy ratio left expected range.")
|
||||
|
||||
# OUTPUT
|
||||
dram_vs_fp32_str = fmt_int(round(dram_vs_fp32.m_as("")), commas=False)
|
||||
dram_vs_fp16_str = fmt_multiple(dram_vs_fp16.m_as(""), precision=1, commas=False)
|
||||
dram_vs_fp32_str = fmt_int(round(dram_vs_fp32.to("").magnitude), commas=False)
|
||||
dram_vs_fp16_str = fmt_multiple(dram_vs_fp16.to("").magnitude, precision=1, commas=False)
|
||||
```
|
||||
|
||||
| **#** | **Principle** | **Part** | **Core Equation/Statement** | **What It Predicts** |
|
||||
@@ -671,15 +671,15 @@ class ConclusionRoofline:
|
||||
|
||||
t_mem_ms_str = fmt_qty(t_mem, ms, precision=1, commas=False)
|
||||
t_comp_ms_str = fmt_qty(t_comp, ms, precision=2, commas=False)
|
||||
ratio_str = fmt_multiple(ratio.m_as(''), precision=1, commas=False)
|
||||
ratio_str = fmt_multiple(ratio.to("").magnitude, precision=1, commas=False)
|
||||
|
||||
# LaTeX equations — use raw magnitudes, not suffixed _str exports
|
||||
dvol_gb_val = d_vol.m_as('GB')
|
||||
h100_bw_gb_val = gpu.memory.bandwidth.m_as('GB/s')
|
||||
compute_gflops_val = compute_req.m_as('GFLOPs')
|
||||
h100_peak_tflops_val = gpu.compute.peak_flops.m_as('TFLOP/s')
|
||||
t_mem_ms_val = t_mem.m_as('ms')
|
||||
t_comp_ms_val = t_comp.m_as('ms')
|
||||
dvol_gb_val = d_vol.to(GB).magnitude
|
||||
h100_bw_gb_val = gpu.memory.bandwidth.to(GB / second).magnitude
|
||||
compute_gflops_val = compute_req.to(GFLOPs).magnitude
|
||||
h100_peak_tflops_val = gpu.compute.peak_flops.to(TFLOP / second).magnitude
|
||||
t_mem_ms_val = t_mem.to(ms).magnitude
|
||||
t_comp_ms_val = t_comp.to(ms).magnitude
|
||||
|
||||
t_mem_eq = fmt_math(f"T_{{\\text{{mem}}}} = \\frac{{{dvol_gb_val:.0f} \\text{{ GB}}}}{{{h100_bw_gb_val:.0f} \\text{{ GB/s}}}} \\approx {t_mem_ms_val:.1f} \\text{{ ms}}")
|
||||
t_comp_eq = fmt_math(f"T_{{\\text{{comp}}}} = \\frac{{{compute_gflops_val:.0f} \\times 10^9}}{{{h100_peak_tflops_val:.0f} \\times 10^{{12}}}} = {t_comp_ms_val:.2f} \\text{{ ms}}")
|
||||
@@ -833,14 +833,14 @@ class EdgeReferenceRatios:
|
||||
memory_ratio = h_h100.memory.capacity / phone_ram
|
||||
|
||||
# GUARD
|
||||
check(int8_peak_ratio.m_as("") > 10, "Mobile edge example should be far below H100 throughput.")
|
||||
check(memory_ratio.m_as("") > 5, "Mobile edge example should have meaningfully less memory headroom.")
|
||||
check(power_ratio.m_as("") > 100, "Mobile edge example should have a much smaller power envelope.")
|
||||
check(int8_peak_ratio.to("").magnitude > 10, "Mobile edge example should be far below H100 throughput.")
|
||||
check(memory_ratio.to("").magnitude > 5, "Mobile edge example should have meaningfully less memory headroom.")
|
||||
check(power_ratio.to("").magnitude > 100, "Mobile edge example should have a much smaller power envelope.")
|
||||
|
||||
# OUTPUT
|
||||
int8_peak_ratio_str = fmt_multiple(int8_peak_ratio.m_as(""), precision=1, commas=False)
|
||||
power_ratio_str = fmt_multiple(power_ratio.m_as(""), precision=1, commas=False)
|
||||
memory_ratio_str = fmt_multiple(memory_ratio.m_as(""), precision=1, commas=False)
|
||||
int8_peak_ratio_str = fmt_multiple(int8_peak_ratio.to("").magnitude, precision=1, commas=False)
|
||||
power_ratio_str = fmt_multiple(power_ratio.to("").magnitude, precision=1, commas=False)
|
||||
memory_ratio_str = fmt_multiple(memory_ratio.to("").magnitude, precision=1, commas=False)
|
||||
```
|
||||
|
||||
\index{AI Democratization!edge deployment}
|
||||
@@ -946,17 +946,17 @@ class FleetFailureMath:
|
||||
gpu_mtbf = float(Systems.Reliability.Gpu.mttf_hours) * ureg.hour
|
||||
|
||||
# EXECUTE
|
||||
gpu_mtbf_years = gpu_mtbf.m_as("hour") / (HOURS_PER_DAY * DAYS_PER_YEAR)
|
||||
gpu_mtbf_years = gpu_mtbf.to(hour).magnitude / (HOURS_PER_DAY * DAYS_PER_YEAR)
|
||||
cluster_mtbf = calc_mtbf_cluster(gpu_mtbf, gpu_count)
|
||||
|
||||
# GUARD
|
||||
check(gpu_mtbf_years > 1, "Component MTTF should read as years.")
|
||||
check(cluster_mtbf.m_as("hour") < 100, "Thousand-GPU MTBF should read as hours.")
|
||||
check(cluster_mtbf.to(hour).magnitude < 100, "Thousand-GPU MTBF should read as hours.")
|
||||
|
||||
# OUTPUT
|
||||
gpu_count_str = fmt(gpu_count, precision=0)
|
||||
gpu_mtbf_years_str = fmt_time(gpu_mtbf_years, 'year', precision=1, commas=False, style='word')
|
||||
cluster_mtbf_hours_str = fmt_time(cluster_mtbf.m_as("hour"), 'hour', precision=1, commas=False, style='word')
|
||||
cluster_mtbf_hours_str = fmt_time(cluster_mtbf.to(hour).magnitude, 'hour', precision=1, commas=False, style='word')
|
||||
```
|
||||
|
||||
### Node to fleet { .unnumbered}
|
||||
|
||||
@@ -56,8 +56,8 @@ from mlsysim.fmt import fmt_int, fmt, fmt_qty, fmt_qty_int, check
|
||||
class HwAccelSetup:
|
||||
"""A100 peak FP16/BF16 throughput vs. server CPU for the opening callout."""
|
||||
# LOAD
|
||||
a100_tflops_fp16 = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
cpu_tflops_low = Hardware.Cloud.ReferenceCPU.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_tflops_fp16 = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
cpu_tflops_low = Hardware.Cloud.ReferenceCPU.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
cpu_tflops_high = 2
|
||||
|
||||
# EXECUTE
|
||||
@@ -276,7 +276,7 @@ class AmdahlH100:
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ──────────────────────────────────────────────
|
||||
# Hardware context
|
||||
h100_tops_int8 = Hardware.Cloud.H100.compute.precision_flops["int8"].m_as(TOPS)
|
||||
h100_tops_int8 = Hardware.Cloud.H100.compute.precision_flops["int8"].to(TOPS).magnitude
|
||||
h100_tops_int8_str = fmt(h100_tops_int8, precision=0)
|
||||
hw_speedup_str = fmt(hw_speedup_factor, precision=0, commas=False)
|
||||
|
||||
@@ -795,10 +795,10 @@ class CpuMlInefficiency:
|
||||
cpu_gflops_str = fmt(cpu_gflops_value, precision=0, commas=False)
|
||||
|
||||
# A100/Mobile specs for footnote comparison
|
||||
a100_tflops_fp16 = f"{Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second):.0f}"
|
||||
_tf32_tflops = Hardware.Cloud.A100.compute.precision_flops['tf32'].m_as(TFLOPs/second)
|
||||
a100_tflops_fp16 = f"{Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude:.0f}"
|
||||
_tf32_tflops = Hardware.Cloud.A100.compute.precision_flops['tf32'].to(TFLOPs/second).magnitude
|
||||
a100_tflops_tf32 = f"{_tf32_tflops:.0f}"
|
||||
mobile_tops = f"{Hardware.Mobile.iPhone15Pro.compute.peak_flops.m_as(TOPS):.0f}"
|
||||
mobile_tops = f"{Hardware.Mobile.iPhone15Pro.compute.peak_flops.to(TOPS).magnitude:.0f}"
|
||||
```
|
||||
|
||||
### Machine learning hardware specialization {#sec-hardware-acceleration-machine-learning-hardware-specialization-09c5}
|
||||
@@ -815,9 +815,9 @@ class MlAcceleratorCallout:
|
||||
"""A100 throughput and bandwidth vs. server CPU for the ML accelerator callout."""
|
||||
|
||||
# LOAD
|
||||
a100_tflops_fp16 = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
cpu_tflops_low = Hardware.Cloud.ReferenceCPU.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_tflops_fp16 = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
cpu_tflops_low = Hardware.Cloud.ReferenceCPU.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
cpu_tflops_high = 2
|
||||
|
||||
# EXECUTE
|
||||
@@ -1993,7 +1993,7 @@ class SystolicEnergy:
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
# Energy value sourced from the mlsysim registry (Hardware.Tech.Memory).
|
||||
ENERGY_DRAM_ACCESS_PJ = Hardware.Tech.Memory.DRAM.energy_per_access
|
||||
dram_pj = ENERGY_DRAM_ACCESS_PJ.m_as('pJ')
|
||||
dram_pj = ENERGY_DRAM_ACCESS_PJ.to(pJ).magnitude
|
||||
mac_pj = 1.0 # Compute cost
|
||||
|
||||
# Vector Unit: Needs 3 loads (A, B, C) + 1 write (C) per MAC
|
||||
@@ -2324,7 +2324,7 @@ class SystolicOpsCalc:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
SYSTOLIC_ARRAY_DIM = 128 # pedagogical systolic-array dimension example
|
||||
systolic_dim_value = SYSTOLIC_ARRAY_DIM
|
||||
tpuv4_bw_gbs = Hardware.Cloud.TPUv4.memory.bandwidth.m_as(GB/second)
|
||||
tpuv4_bw_gbs = Hardware.Cloud.TPUv4.memory.bandwidth.to(GB/second).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
systolic_ops_value = systolic_dim_value * systolic_dim_value
|
||||
@@ -2415,10 +2415,10 @@ class InterconnectBandwidth:
|
||||
"""Namespace for Interconnect Hierarchy bandwidth taper."""
|
||||
h_h100 = Hardware.Cloud.H100
|
||||
|
||||
hbm_bw = h_h100.memory.bandwidth.m_as(GB/second)
|
||||
nvlink_bw = Hardware.Cloud.H100.nvlink.bandwidth.m_as(GB/second)
|
||||
pcie_bw = Hardware.Cloud.H100.interconnect.bandwidth.m_as(GB/second)
|
||||
net_bw = Systems.Fabrics.InfiniBand_NDR.bandwidth.m_as(GB/second)
|
||||
hbm_bw = h_h100.memory.bandwidth.to(GB/second).magnitude
|
||||
nvlink_bw = Hardware.Cloud.H100.nvlink.bandwidth.to(GB/second).magnitude
|
||||
pcie_bw = Hardware.Cloud.H100.interconnect.bandwidth.to(GB/second).magnitude
|
||||
net_bw = Systems.Fabrics.InfiniBand_NDR.bandwidth.to(GB/second).magnitude
|
||||
|
||||
# Invariant: HBM > NVLink > PCIe > Network
|
||||
check(hbm_bw > nvlink_bw > pcie_bw > net_bw, "Bandwidth taper violated!")
|
||||
@@ -2510,27 +2510,27 @@ class AcceleratorEconomics:
|
||||
h_h100 = Hardware.Cloud.H100
|
||||
h_tpu = Hardware.Cloud.TPUv4
|
||||
|
||||
price_v100 = Hardware.Cloud.V100.unit_cost.m_as("dollar") # older generation
|
||||
price_a100 = Hardware.Cloud.A100.unit_cost.m_as("dollar") # current workhorse
|
||||
price_v100 = Hardware.Cloud.V100.unit_cost.to(USD).magnitude # older generation
|
||||
price_a100 = Hardware.Cloud.A100.unit_cost.to(USD).magnitude # current workhorse
|
||||
price_h100 = 25000 # lower bound of range
|
||||
price_tpu = 8000 # estimated from cloud rates
|
||||
price_gaudi = 12000 # Intel alternative
|
||||
|
||||
# Gaudi 2 (FP8 — Gaudi 2's flagship precision)
|
||||
gaudi_tf = Hardware.Cloud.Gaudi2.compute.precision_flops["fp8"].m_as(TFLOPs/second)
|
||||
gaudi_bw_value = Hardware.Cloud.Gaudi2.memory.bandwidth.m_as(GB/second)
|
||||
gaudi_tf = Hardware.Cloud.Gaudi2.compute.precision_flops["fp8"].to(TFLOPs/second).magnitude
|
||||
gaudi_bw_value = Hardware.Cloud.Gaudi2.memory.bandwidth.to(GB/second).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
v100_tf = h_v100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
v100_tf = h_v100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
v100_ratio = price_v100 / v100_tf
|
||||
|
||||
a100_tf = h_a100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_tf = h_a100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
a100_ratio = price_a100 / a100_tf
|
||||
|
||||
h100_tf = h_h100.compute.precision_flops["tf32"].m_as(TFLOPs/second)
|
||||
h100_tf = h_h100.compute.precision_flops["tf32"].to(TFLOPs/second).magnitude
|
||||
h100_ratio = price_h100 / h100_tf
|
||||
|
||||
tpu_tf = h_tpu.compute.peak_flops.m_as(TFLOPs/second)
|
||||
tpu_tf = h_tpu.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
tpu_ratio = price_tpu/tpu_tf
|
||||
|
||||
gaudi_ratio = price_gaudi/gaudi_tf
|
||||
@@ -2552,8 +2552,8 @@ class AcceleratorEconomics:
|
||||
|
||||
# H100 specs
|
||||
h100_tflops_tf32_str = fmt_qty(h_h100.compute.precision_flops["tf32"], TFLOP/second, precision=0, commas=False)
|
||||
h100_tflops_fp16_str = fmt(h_h100.compute.peak_flops.m_as(TFLOPs/second), precision=0, commas=False)
|
||||
h100_tflops_fp8_str = fmt(Hardware.Cloud.H100.compute.precision_flops["fp8"].m_as(TFLOPs/second), precision=0)
|
||||
h100_tflops_fp16_str = fmt(h_h100.compute.peak_flops.to(TFLOPs/second).magnitude, precision=0, commas=False)
|
||||
h100_tflops_fp8_str = fmt(Hardware.Cloud.H100.compute.precision_flops["fp8"].to(TFLOPs/second).magnitude, precision=0)
|
||||
h100_bw_str = fmt_qty(h_h100.memory.bandwidth, GB/second, precision=0, commas=True)
|
||||
h100_bw_tbs_str = fmt_qty(h_h100.memory.bandwidth, TB/second, precision=2, commas=False)
|
||||
h100_price_str = fmt_usd_range(price_h100, 30000, precision=0, approx=True, repeat_symbol=False)
|
||||
@@ -2607,7 +2607,7 @@ from mlsysim.fmt import fmt
|
||||
|
||||
class B200FP4Specs:
|
||||
"""B200 FP4 peak throughput (dense and sparse)."""
|
||||
dense_pflops = Hardware.Cloud.B200.compute.precision_flops["fp4"].m_as(PFLOPs/second)
|
||||
dense_pflops = Hardware.Cloud.B200.compute.precision_flops["fp4"].to(PFLOPs/second).magnitude
|
||||
sparse_pflops = 2 * dense_pflops
|
||||
dense_pflops_str = fmt_qty(dense_pflops * PFLOPs / second, PFLOPs / second, precision=0, commas=False, unit_label="PFLOP/s")
|
||||
sparse_pflops_str = fmt_qty(sparse_pflops * PFLOPs / second, PFLOPs / second, precision=0, commas=False, unit_label="PFLOP/s")
|
||||
@@ -2722,8 +2722,8 @@ from mlsysim.fmt import fmt
|
||||
class MemoryWallH100Specs:
|
||||
"""H100 peak throughput and bandwidth for the memory-wall scaling prose."""
|
||||
|
||||
h100_tflops_fp16 = Hardware.Cloud.H100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
h100_bw_tbs = Hardware.Cloud.H100.memory.bandwidth.m_as(TB/second)
|
||||
h100_tflops_fp16 = Hardware.Cloud.H100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
h100_bw_tbs = Hardware.Cloud.H100.memory.bandwidth.to(TB/second).magnitude
|
||||
|
||||
h100_tflops_fp16_str = fmt(h100_tflops_fp16, precision=0, commas=False)
|
||||
h100_bw_tbs_str = fmt_qty(h100_bw_tbs * TB / second, TB / second, precision=2, commas=False)
|
||||
@@ -2841,10 +2841,10 @@ fig, ax, COLORS, plt = viz.setup_plot(figsize=(10, 4.5))
|
||||
years = [2017, 2020, 2022, 2024]
|
||||
chips = ['V100', 'A100', 'H100', 'B200']
|
||||
ridges = [
|
||||
Hardware.Cloud.V100.compute.peak_flops.m_as(TFLOPs/second) / Hardware.Cloud.V100.memory.bandwidth.m_as(TB/second),
|
||||
Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second) / Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second),
|
||||
Hardware.Cloud.H100.compute.peak_flops.m_as(TFLOPs/second) / Hardware.Cloud.H100.memory.bandwidth.m_as(TB/second),
|
||||
Hardware.Cloud.B200.compute.peak_flops.m_as(TFLOPs/second) / Hardware.Cloud.B200.memory.bandwidth.m_as(TB/second),
|
||||
Hardware.Cloud.V100.compute.peak_flops.to(TFLOPs/second).magnitude / Hardware.Cloud.V100.memory.bandwidth.to(TB/second).magnitude,
|
||||
Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude / Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude,
|
||||
Hardware.Cloud.H100.compute.peak_flops.to(TFLOPs/second).magnitude / Hardware.Cloud.H100.memory.bandwidth.to(TB/second).magnitude,
|
||||
Hardware.Cloud.B200.compute.peak_flops.to(TFLOPs/second).magnitude / Hardware.Cloud.B200.memory.bandwidth.to(TB/second).magnitude,
|
||||
]
|
||||
|
||||
# =============================================================================
|
||||
@@ -3008,21 +3008,21 @@ class TensorLifecycleCalc:
|
||||
# Energy value sourced from the mlsysim registry (Hardware.Tech.Memory).
|
||||
ENERGY_DRAM_PJ_PER_BYTE = Hardware.Tech.Memory.DRAM.energy_per_byte
|
||||
kws_samples_value = 16_000
|
||||
kws_bytes_fp16_value = BYTES_FP16.m_as('B')
|
||||
kws_bytes_fp16_value = BYTES_FP16.to(byte).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
kws_tensor_kb_value = (kws_samples_value * BYTES_FP16).m_as(KB)
|
||||
dram_energy_pj_bit_value = ENERGY_DRAM_PJ_PER_BYTE.m_as('pJ/B') / 8
|
||||
a100_fp16_tflops_value = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
kws_tensor_kb_value = (kws_samples_value * BYTES_FP16).to(KB).magnitude
|
||||
dram_energy_pj_bit_value = ENERGY_DRAM_PJ_PER_BYTE.to(pJ / byte).magnitude / 8
|
||||
a100_fp16_tflops_value = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
kws_tensor_str = fmt_qty(kws_tensor_kb_value * KB, KB, precision=0, commas=False)
|
||||
kws_samples_str = fmt(kws_samples_value, precision=0, commas=True)
|
||||
kws_bytes_str = fmt(kws_bytes_fp16_value, precision=0, commas=False)
|
||||
dram_energy_pj_bit_str = fmt(dram_energy_pj_bit_value, precision=0, commas=False)
|
||||
latency_hbm_str = fmt(Hardware.Tech.Memory.DRAM.latency.m_as('ns'), precision=0, commas=False)
|
||||
latency_l2_str = fmt(Hardware.Tech.Memory.L2.latency.m_as('ns'), precision=0, commas=False)
|
||||
latency_l1_str = fmt(Hardware.Tech.Memory.L1.latency.m_as('ns'), precision=0, commas=False)
|
||||
latency_hbm_str = fmt(Hardware.Tech.Memory.DRAM.latency.to(NS).magnitude, precision=0, commas=False)
|
||||
latency_l2_str = fmt(Hardware.Tech.Memory.L2.latency.to(NS).magnitude, precision=0, commas=False)
|
||||
latency_l1_str = fmt(Hardware.Tech.Memory.L1.latency.to(NS).magnitude, precision=0, commas=False)
|
||||
a100_tflops_fp16_str = fmt_qty(a100_fp16_tflops_value * TFLOP / second, TFLOP / second, precision=0, commas=False)
|
||||
# Plain-string literal → MarkdownStr per recipe Rule 1 (plain string literal _str)
|
||||
reg_energy_pj_bit_str = MarkdownStr("0.1")
|
||||
@@ -3486,20 +3486,20 @@ class InterconnectHierarchy:
|
||||
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
# Device
|
||||
hbm_bw = Hardware.Cloud.A100.memory.bandwidth.m_as(GB/second)
|
||||
hbm_bw = Hardware.Cloud.A100.memory.bandwidth.to(GB/second).magnitude
|
||||
|
||||
# Chip-to-Chip
|
||||
nvlink_a100 = Hardware.Cloud.A100.nvlink.bandwidth.m_as(GB/second)
|
||||
nvlink_h100 = Hardware.Cloud.H100.nvlink.bandwidth.m_as(GB/second)
|
||||
nvlink_a100 = Hardware.Cloud.A100.nvlink.bandwidth.to(GB/second).magnitude
|
||||
nvlink_h100 = Hardware.Cloud.H100.nvlink.bandwidth.to(GB/second).magnitude
|
||||
|
||||
# Host-to-Device
|
||||
pcie_gen4 = Hardware.Cloud.A100.interconnect.bandwidth.m_as(GB/second)
|
||||
pcie_gen4 = Hardware.Cloud.A100.interconnect.bandwidth.to(GB/second).magnitude
|
||||
|
||||
# Node-to-Node (Network)
|
||||
ib_hdr_gbps = Systems.Fabrics.InfiniBand_HDR.bandwidth.m_as(Gbps)
|
||||
ib_hdr_gbs = Systems.Fabrics.InfiniBand_HDR.bandwidth.m_as(GB/second) # ~25 GB/s
|
||||
ib_hdr_gbps = Systems.Fabrics.InfiniBand_HDR.bandwidth.to(Gbps).magnitude
|
||||
ib_hdr_gbs = Systems.Fabrics.InfiniBand_HDR.bandwidth.to(GB/second).magnitude # ~25 GB/s
|
||||
|
||||
ib_ndr_gbps = Systems.Fabrics.InfiniBand_NDR.bandwidth.m_as(Gbps)
|
||||
ib_ndr_gbps = Systems.Fabrics.InfiniBand_NDR.bandwidth.to(Gbps).magnitude
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
# The "bandwidth taper" must hold: HBM > NVLink > PCIe > Network
|
||||
@@ -3514,10 +3514,10 @@ class InterconnectHierarchy:
|
||||
|
||||
# Bandwidth-taper equation — one consistent chip (H100-class) so HBM, NVLink,
|
||||
# PCIe (Gen5), and the NDR network link cannot mix incompatible generations.
|
||||
taper_hbm_gbs = Hardware.Cloud.H100.memory.bandwidth.m_as(GB/second)
|
||||
taper_hbm_gbs = Hardware.Cloud.H100.memory.bandwidth.to(GB/second).magnitude
|
||||
taper_nvlink_gbs = nvlink_h100
|
||||
taper_pcie_gbs = Hardware.Cloud.H100.interconnect.bandwidth.m_as(GB/second)
|
||||
taper_net_gbs = Systems.Fabrics.InfiniBand_NDR.bandwidth.m_as(GB/second)
|
||||
taper_pcie_gbs = Hardware.Cloud.H100.interconnect.bandwidth.to(GB/second).magnitude
|
||||
taper_net_gbs = Systems.Fabrics.InfiniBand_NDR.bandwidth.to(GB/second).magnitude
|
||||
check(taper_hbm_gbs > taper_nvlink_gbs > taper_pcie_gbs > taper_net_gbs,
|
||||
"bandwidth taper HBM > NVLink > PCIe > Network must hold")
|
||||
taper_eq_math = MarkdownStr(
|
||||
@@ -3644,7 +3644,7 @@ The key metric that determines which ceiling a workload hits is *arithmetic inte
|
||||
# │ Goal: Demonstrate why newer accelerators are harder to saturate each generation.
|
||||
# │ Show: Ridge points ~139 FLOP/byte (V100), ~153 (A100), ~296 (H100); rising gap.
|
||||
# │ How: ridge_point() = peak_flops/memory_bw via Hardware Digital Twin; both in
|
||||
# │ SI-compatible pint units so .m_as('flop/byte') extracts dimensionless ratio.
|
||||
# │ SI-compatible pint units so .to(flop/byte).magnitude extracts dimensionless ratio.
|
||||
# │
|
||||
# │ Imports: mlsysim.core.constants (V100/A100/H100 peak_flops, memory_bw,
|
||||
# │ flop, byte, GB, TB, TFLOPs, second)
|
||||
@@ -3685,23 +3685,23 @@ class RooflineGap:
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
# Step 1: Ridge Points (FLOP/byte) directly from Twins
|
||||
v100_ridge = h_v100.ridge_point().m_as('flop/byte')
|
||||
a100_ridge = h_a100.ridge_point().m_as('flop/byte')
|
||||
h100_ridge = h_h100.ridge_point().m_as('flop/byte')
|
||||
v100_ridge = h_v100.ridge_point().to(flop/byte).magnitude
|
||||
a100_ridge = h_a100.ridge_point().to(flop/byte).magnitude
|
||||
h100_ridge = h_h100.ridge_point().to(flop/byte).magnitude
|
||||
|
||||
# Step 2: FP32 Ridge for A100
|
||||
a100_ridge_fp32 = (h_a100.compute.precision_flops['fp32'] / h_a100.memory.bandwidth).m_as('flop/byte')
|
||||
a100_ridge_fp32 = (h_a100.compute.precision_flops['fp32'] / h_a100.memory.bandwidth).to(flop/byte).magnitude
|
||||
|
||||
# Step 3: Display specs used by the nearby ridge-point prose
|
||||
v100_tflops_value = h_v100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
v100_bw_tbs_value = h_v100.memory.bandwidth.m_as(TB/second)
|
||||
a100_tflops_fp16_value = h_a100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_bw_tbs_value = h_a100.memory.bandwidth.m_as(TB/second)
|
||||
h100_tflops_fp16_value = h_h100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
h100_bw_tbs_value = h_h100.memory.bandwidth.m_as(TB/second)
|
||||
v100_tflops_value = h_v100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
v100_bw_tbs_value = h_v100.memory.bandwidth.to(TB/second).magnitude
|
||||
a100_tflops_fp16_value = h_a100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
a100_bw_tbs_value = h_a100.memory.bandwidth.to(TB/second).magnitude
|
||||
h100_tflops_fp16_value = h_h100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
h100_bw_tbs_value = h_h100.memory.bandwidth.to(TB/second).magnitude
|
||||
|
||||
# Step 4: Comparisons
|
||||
bw_growth = h_h100.memory.bandwidth.m_as(GB/second) / h_a100.memory.bandwidth.m_as(GB/second)
|
||||
bw_growth = h_h100.memory.bandwidth.to(GB/second).magnitude / h_a100.memory.bandwidth.to(GB/second).magnitude
|
||||
flops_growth = h_h100.compute.peak_flops/h_a100.compute.peak_flops
|
||||
relu_gap = h100_ridge/relu_ai
|
||||
dense_matmul_flops = 2 * dense_matmul_dim ** 3
|
||||
@@ -3838,28 +3838,28 @@ class TransformerLayerCalc:
|
||||
t_batch_value = 32
|
||||
t_seq_value = 512
|
||||
t_heads_value = 12
|
||||
t_fp_bytes_value = BYTES_FP16.m_as(byte)
|
||||
t_fp_bytes_value = BYTES_FP16.to(byte).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
# QKV Projection
|
||||
qkv_flops_value = 2 * 3 * t_batch_value * t_seq_value * t_hidden_value * t_hidden_value
|
||||
qkv_flops_b_value = (qkv_flops_value * flop).m_as(GFLOPs)
|
||||
qkv_flops_b_value = (qkv_flops_value * flop).to(GFLOPs).magnitude
|
||||
|
||||
qkv_input_value = t_batch_value * t_seq_value * t_hidden_value
|
||||
qkv_weights_value = 3 * t_hidden_value * t_hidden_value
|
||||
qkv_output_value = t_batch_value * t_seq_value * t_hidden_value * 3
|
||||
qkv_bytes_value = (qkv_input_value + qkv_weights_value + qkv_output_value) * t_fp_bytes_value
|
||||
qkv_mb_value = (qkv_bytes_value * byte).m_as(MB)
|
||||
qkv_mb_value = (qkv_bytes_value * byte).to(MB).magnitude
|
||||
qkv_ai_value = qkv_flops_value/qkv_bytes_value
|
||||
|
||||
# Softmax
|
||||
softmax_flops_value = t_batch_value * t_heads_value * t_seq_value * t_seq_value * 3
|
||||
softmax_flops_m_value = (softmax_flops_value * flop).m_as(MFLOPs)
|
||||
softmax_flops_m_value = (softmax_flops_value * flop).to(MFLOPs).magnitude
|
||||
softmax_bytes_value = t_batch_value * t_heads_value * t_seq_value * t_seq_value * 2 * t_fp_bytes_value
|
||||
softmax_mb_value = (softmax_bytes_value * byte).m_as(MB)
|
||||
softmax_mb_value = (softmax_bytes_value * byte).to(MB).magnitude
|
||||
softmax_ai_value = softmax_flops_value/softmax_bytes_value
|
||||
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().m_as('flop/byte')
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().to(flop/byte).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
t_hidden_str = fmt(t_hidden_value, precision=0, commas=False)
|
||||
@@ -3944,23 +3944,23 @@ class Conv2dAnalysisCalc:
|
||||
conv_w = 56
|
||||
conv_cout = 256
|
||||
conv_k = 3
|
||||
conv_fp_bytes = BYTES_FP16.m_as('B')
|
||||
conv_fp_bytes = BYTES_FP16.to(byte).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
conv_out_elements = conv_batch * conv_cout * conv_h * conv_w
|
||||
conv_out_m = conv_out_elements/MILLION
|
||||
conv_flops_per_out = conv_cin * conv_k * conv_k * 2
|
||||
conv_total_gflops = (conv_out_elements * conv_flops_per_out * flop).m_as(GFLOPs)
|
||||
conv_total_gflops = (conv_out_elements * conv_flops_per_out * flop).to(GFLOPs).magnitude
|
||||
|
||||
conv_input_mb = (conv_batch * conv_cin * conv_h * conv_w * conv_fp_bytes * byte).m_as(MB)
|
||||
conv_weights_mb = (conv_cout * conv_cin * conv_k * conv_k * conv_fp_bytes * byte).m_as(MB)
|
||||
conv_output_mb = (conv_batch * conv_cout * conv_h * conv_w * conv_fp_bytes * byte).m_as(MB)
|
||||
conv_input_mb = (conv_batch * conv_cin * conv_h * conv_w * conv_fp_bytes * byte).to(MB).magnitude
|
||||
conv_weights_mb = (conv_cout * conv_cin * conv_k * conv_k * conv_fp_bytes * byte).to(MB).magnitude
|
||||
conv_output_mb = (conv_batch * conv_cout * conv_h * conv_w * conv_fp_bytes * byte).to(MB).magnitude
|
||||
conv_total_mb = conv_input_mb + conv_weights_mb + conv_output_mb
|
||||
|
||||
conv_ai = conv_total_gflops * THOUSAND / conv_total_mb
|
||||
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().m_as('flop/byte')
|
||||
a100_tflops_fp16_value = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().to(flop/byte).magnitude
|
||||
a100_tflops_fp16_value = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
conv_out_m_str = fmt(conv_out_m, precision=1, commas=False)
|
||||
@@ -4040,21 +4040,21 @@ class DenseLayerAnalysisCalc:
|
||||
dense_out = 2048
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
dense_total_mflops = (2 * dense_batch * dense_in * dense_out * flop).m_as(MFLOPs)
|
||||
dense_total_mflops = (2 * dense_batch * dense_in * dense_out * flop).to(MFLOPs).magnitude
|
||||
|
||||
dense_input_kb = (dense_batch * dense_in * 2 * byte).m_as(KB)
|
||||
dense_weights_mb = (dense_in * dense_out * 2 * byte).m_as(MB)
|
||||
dense_output_kb = (dense_batch * dense_out * 2 * byte).m_as(KB)
|
||||
dense_total_mb = (dense_input_kb * KB + dense_weights_mb * MB + dense_output_kb * KB).m_as(MB)
|
||||
dense_input_kb = (dense_batch * dense_in * 2 * byte).to(KB).magnitude
|
||||
dense_weights_mb = (dense_in * dense_out * 2 * byte).to(MB).magnitude
|
||||
dense_output_kb = (dense_batch * dense_out * 2 * byte).to(KB).magnitude
|
||||
dense_total_mb = (dense_input_kb * KB + dense_weights_mb * MB + dense_output_kb * KB).to(MB).magnitude
|
||||
|
||||
dense_ai = dense_total_mflops/dense_total_mb
|
||||
|
||||
a100_bw_gbs_value = Hardware.Cloud.A100.memory.bandwidth.m_as(GB/second)
|
||||
a100_peak = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
dense_attainable_tflops = (a100_bw_gbs_value * dense_ai * GFLOPs).m_as(TFLOPs)
|
||||
a100_bw_gbs_value = Hardware.Cloud.A100.memory.bandwidth.to(GB/second).magnitude
|
||||
a100_peak = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
dense_attainable_tflops = (a100_bw_gbs_value * dense_ai * GFLOPs).to(TFLOPs).magnitude
|
||||
dense_util_pct = dense_attainable_tflops/a100_peak * 100
|
||||
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().m_as('flop/byte')
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().to(flop/byte).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
dense_total_mflops_str = fmt(dense_total_mflops, precision=1, commas=False)
|
||||
@@ -4140,16 +4140,16 @@ class LayernormAnalysisCalc:
|
||||
ln_flops_per = 6
|
||||
ln_total_mflops = ln_elements_m * ln_flops_per
|
||||
|
||||
ln_input_mb = (ln_elements * 2 * byte).m_as(MB)
|
||||
ln_params_kb = (ln_hidden * 2 * 2 * byte).m_as(KB)
|
||||
ln_output_mb = (ln_elements * 2 * byte).m_as(MB)
|
||||
ln_total_mb = (ln_input_mb * MB + ln_output_mb * MB + ln_params_kb * KB).m_as(MB)
|
||||
ln_input_mb = (ln_elements * 2 * byte).to(MB).magnitude
|
||||
ln_params_kb = (ln_hidden * 2 * 2 * byte).to(KB).magnitude
|
||||
ln_output_mb = (ln_elements * 2 * byte).to(MB).magnitude
|
||||
ln_total_mb = (ln_input_mb * MB + ln_output_mb * MB + ln_params_kb * KB).to(MB).magnitude
|
||||
|
||||
ln_ai = ln_total_mflops/ln_total_mb
|
||||
|
||||
a100_bw_gbs_value = Hardware.Cloud.A100.memory.bandwidth.m_as(GB/second)
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().m_as('flop/byte')
|
||||
ln_attainable_tflops = (a100_bw_gbs_value * ln_ai * GFLOPs).m_as(TFLOPs)
|
||||
a100_bw_gbs_value = Hardware.Cloud.A100.memory.bandwidth.to(GB/second).magnitude
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().to(flop/byte).magnitude
|
||||
ln_attainable_tflops = (a100_bw_gbs_value * ln_ai * GFLOPs).to(TFLOPs).magnitude
|
||||
ln_ridge_gap = a100_ridge_value / ln_ai
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
@@ -4312,15 +4312,15 @@ class Gpt2ThroughputCalc:
|
||||
gpt2_weight_gb = model_memory(Models.Language.GPT2.parameters, BYTES_FP16, GB)
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
gpt2_decode_flops = 2 * Models.Language.GPT2.parameters.m_as('param')
|
||||
gpt2_decode_gflops = (gpt2_decode_flops * flop).m_as(GFLOPs)
|
||||
gpt2_decode_flops = 2 * Models.Language.GPT2.parameters.to(param).magnitude
|
||||
gpt2_decode_gflops = (gpt2_decode_flops * flop).to(GFLOPs).magnitude
|
||||
gpt2_decode_ai = gpt2_decode_gflops/gpt2_weight_gb
|
||||
|
||||
a100_bw_tbs_val = Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
a100_tflops_fp16_val = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_tflops_fp32_val = Hardware.Cloud.A100.compute.precision_flops["fp32"].m_as(TFLOPs/second)
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().m_as('flop/byte')
|
||||
a100_ridge_fp32_value = (Hardware.Cloud.A100.compute.precision_flops['fp32'] / Hardware.Cloud.A100.memory.bandwidth).m_as('flop/byte')
|
||||
a100_bw_tbs_val = Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
a100_tflops_fp16_val = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
a100_tflops_fp32_val = Hardware.Cloud.A100.compute.precision_flops["fp32"].to(TFLOPs/second).magnitude
|
||||
a100_ridge_value = Hardware.Cloud.A100.ridge_point().to(flop/byte).magnitude
|
||||
a100_ridge_fp32_value = (Hardware.Cloud.A100.compute.precision_flops['fp32'] / Hardware.Cloud.A100.memory.bandwidth).to(flop/byte).magnitude
|
||||
gpt2_max_tflops = gpt2_decode_ai * a100_bw_tbs_val
|
||||
gpt2_utilization = gpt2_max_tflops/a100_tflops_fp16_val * 100
|
||||
|
||||
@@ -4831,11 +4831,11 @@ class MemoryFootprintCalc:
|
||||
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
tensor_dim = 1024
|
||||
bytes_fp32 = BYTES_FP32.m_as('B')
|
||||
bytes_fp32 = BYTES_FP32.to(byte).magnitude
|
||||
n_intermediates = 4 # X, X', X'', Y tensors stored
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
tensor_mb = (tensor_dim * tensor_dim * bytes_fp32 * byte).m_as(MB)
|
||||
tensor_mb = (tensor_dim * tensor_dim * bytes_fp32 * byte).to(MB).magnitude
|
||||
total_mb = n_intermediates * tensor_mb
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -4920,10 +4920,10 @@ class FusionBenefitsCalc:
|
||||
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
tensor_dim = 1024
|
||||
bytes_per_float = BYTES_FP32.m_as('B')
|
||||
bytes_per_float = BYTES_FP32.to(byte).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
tensor_mb = (tensor_dim * tensor_dim * bytes_per_float * byte).m_as(MB)
|
||||
tensor_mb = (tensor_dim * tensor_dim * bytes_per_float * byte).to(MB).magnitude
|
||||
total_mb = tensor_mb * 4
|
||||
|
||||
naive_mb = total_mb # ~16.8 MB with all four intermediates stored
|
||||
@@ -5489,7 +5489,7 @@ class RuntimeProductionTdp:
|
||||
# (constant lookup, no derivation)
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
a100_tdp_str = fmt(Hardware.Cloud.A100.tdp.m_as(watt), precision=0, commas=False)
|
||||
a100_tdp_str = fmt(Hardware.Cloud.A100.tdp.to(watt).magnitude, precision=0, commas=False)
|
||||
```
|
||||
|
||||
::: {#psp-hw-acceleration-when-production-differs-from-development .callout-perspective title="When production differs from development"}
|
||||
@@ -5568,7 +5568,7 @@ class Gpt3SingleH100Calc:
|
||||
peak_flops_per_device=Hardware.Cloud.H100.compute.precision_flops["fp8"],
|
||||
efficiency_eta=1.0
|
||||
)
|
||||
peak_seconds = peak_seconds_qty.m_as(second)
|
||||
peak_seconds = peak_seconds_qty.to(second).magnitude
|
||||
peak_years = peak_seconds / SEC_PER_YEAR
|
||||
|
||||
realistic_util_low = 0.40
|
||||
@@ -5580,7 +5580,7 @@ class Gpt3SingleH100Calc:
|
||||
check(4.5 < peak_years < 5.5, f"Single-H100 FP8 peak time should be ~5 years, got {peak_years:.2f}")
|
||||
|
||||
# OUTPUT
|
||||
h100_pflops_fp8_str = fmt_int(Hardware.Cloud.H100.compute.precision_flops["fp8"].m_as(PFLOPs/second), commas=False)
|
||||
h100_pflops_fp8_str = fmt_int(Hardware.Cloud.H100.compute.precision_flops["fp8"].to(PFLOPs/second).magnitude, commas=False)
|
||||
peak_years_str = fmt_int(round(peak_years), commas=False)
|
||||
realistic_years_low_str = fmt(realistic_years_low, precision=1, commas=False)
|
||||
realistic_years_high_str = fmt(realistic_years_high, precision=1, commas=False)
|
||||
@@ -5717,8 +5717,8 @@ class FpWorkloadFitRecap:
|
||||
"""A100 roofline recap for the hardware-specialization fallacy."""
|
||||
|
||||
# LOAD
|
||||
a100_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
a100_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
softmax_ai_low = 2
|
||||
softmax_ai_high = 5
|
||||
cpu_ridge_low = 10
|
||||
@@ -5783,8 +5783,8 @@ class FpMemoryEnergyCalc:
|
||||
# Energy values sourced from the mlsysim registry (Hardware.Tech.Memory).
|
||||
ENERGY_DRAM_ACCESS_PJ = Hardware.Tech.Memory.DRAM.energy_per_access
|
||||
ENERGY_SRAM_L1_PJ = Hardware.Tech.Memory.L1.energy_per_access
|
||||
dram_pj = ENERGY_DRAM_ACCESS_PJ.m_as('pJ')
|
||||
sram_pj = ENERGY_SRAM_L1_PJ.m_as('pJ')
|
||||
dram_pj = ENERGY_DRAM_ACCESS_PJ.to(pJ).magnitude
|
||||
sram_pj = ENERGY_SRAM_L1_PJ.to(pJ).magnitude
|
||||
|
||||
layernorm_ai = 1.5
|
||||
peak_tflops = 300 # hypothetical accelerator
|
||||
@@ -5838,7 +5838,7 @@ class FpMultigpuScalingCalc:
|
||||
"""NVLink gradient-sync overhead quantifying sublinear multi-GPU scaling."""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
nvlink_bw_gbs = Hardware.Cloud.A100.nvlink.bandwidth.m_as(GB/second)
|
||||
nvlink_bw_gbs = Hardware.Cloud.A100.nvlink.bandwidth.to(GB/second).magnitude
|
||||
gradient_size_gb = 1.0
|
||||
step_time_ms = 50
|
||||
gpu_count = 8
|
||||
@@ -5870,7 +5870,7 @@ class FpPeakVsSustainedRecap:
|
||||
"""A100 peak and sustained ranges for the peak-FLOP/s fallacy."""
|
||||
|
||||
# LOAD
|
||||
a100_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
transformer_tflops_low = 120
|
||||
transformer_tflops_high = 180
|
||||
recsys_tflops_low = 10
|
||||
@@ -5955,11 +5955,11 @@ class FpSmallBatchCalc:
|
||||
ai_b256 = flops_b256 / bytes_b256
|
||||
|
||||
# T4 ridge point
|
||||
t4_flops = Hardware.Cloud.T4.compute.peak_flops.m_as(TFLOPs/second)
|
||||
t4_bw = Hardware.Cloud.T4.memory.bandwidth.m_as(GB/second) # for "GB/s" prose display
|
||||
a100_bw = Hardware.Cloud.A100.memory.bandwidth.m_as(GB/second)
|
||||
t4_bw_tbs = Hardware.Cloud.T4.memory.bandwidth.m_as(TB/second) # for dimensional roofline math
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
t4_flops = Hardware.Cloud.T4.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
t4_bw = Hardware.Cloud.T4.memory.bandwidth.to(GB/second).magnitude # for "GB/s" prose display
|
||||
a100_bw = Hardware.Cloud.A100.memory.bandwidth.to(GB/second).magnitude
|
||||
t4_bw_tbs = Hardware.Cloud.T4.memory.bandwidth.to(TB/second).magnitude # for dimensional roofline math
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
t4_ridge = t4_flops / t4_bw_tbs # TFLOP/s / (TB/s) = FLOP/byte
|
||||
a100_roof_b1 = a100_bw_tbs * ai_b1 # (TB/s) × (FLOP/byte) = TFLOP/s
|
||||
t4_roof_b1 = t4_bw_tbs * ai_b1 # same
|
||||
@@ -6203,8 +6203,8 @@ class SummaryRooflineRecap:
|
||||
"""A100 ridge-point recap for the final takeaways."""
|
||||
|
||||
# LOAD
|
||||
a100_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.m_as(TB/second)
|
||||
a100_peak_tflops = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
a100_bw_tbs = Hardware.Cloud.A100.memory.bandwidth.to(TB/second).magnitude
|
||||
|
||||
# EXECUTE
|
||||
a100_ridge = a100_peak_tflops / a100_bw_tbs
|
||||
|
||||
@@ -2110,7 +2110,7 @@ class KVCacheFootprint:
|
||||
batch_size=1,
|
||||
bytes_per_elem=bytes_per_element
|
||||
)
|
||||
kv_cache_gb = kv_cache_one.m_as(GB)
|
||||
kv_cache_gb = kv_cache_one.to(GB).magnitude
|
||||
concurrent_8_gb = 8 * kv_cache_gb
|
||||
concurrent_8_q = 8 * kv_cache_one
|
||||
|
||||
@@ -2124,7 +2124,7 @@ class KVCacheFootprint:
|
||||
kv_heads_str = fmt(kv_heads, precision=0, commas=False)
|
||||
seq_len_str = fmt(seq_len, precision=0, commas=True)
|
||||
head_dim_str = fmt(head_dim, precision=0, commas=False)
|
||||
bytes_per_element_str = fmt(bytes_per_element.m_as(byte), precision=0, commas=False)
|
||||
bytes_per_element_str = fmt(bytes_per_element.to(byte).magnitude, precision=0, commas=False)
|
||||
kv_cache_gb_str = fmt_qty(kv_cache_one, GB, precision=1, commas=False)
|
||||
concurrent_8_gb_str = fmt_qty(concurrent_8_q, GB, precision=1, commas=False)
|
||||
```
|
||||
@@ -2440,13 +2440,13 @@ class ObservabilitySampling:
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
high_bytes_per_second = requests_per_second * telemetry_per_request / high_frequency_seconds
|
||||
low_bytes_per_second = requests_per_second * telemetry_per_request / low_frequency_seconds
|
||||
sampling_ratio = (high_bytes_per_second / low_bytes_per_second).m_as("")
|
||||
sampling_ratio = (high_bytes_per_second / low_bytes_per_second).to("").magnitude
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
check(round(high_bytes_per_second.m_as(GB), 1) == 1.0,
|
||||
f"Expected ~1 GB/s, got {high_bytes_per_second.m_as(GB):.2f} GB/s.")
|
||||
check(round(low_bytes_per_second.m_as(MB), 1) == 16.7,
|
||||
f"Expected ~16.7 MB/s, got {low_bytes_per_second.m_as(MB):.2f} MB/s.")
|
||||
check(round(high_bytes_per_second.to(GB).magnitude, 1) == 1.0,
|
||||
f"Expected ~1 GB/s, got {high_bytes_per_second.to(GB).magnitude:.2f} GB/s.")
|
||||
check(round(low_bytes_per_second.to(MB).magnitude, 1) == 16.7,
|
||||
f"Expected ~16.7 MB/s, got {low_bytes_per_second.to(MB).magnitude:.2f} MB/s.")
|
||||
check(round(sampling_ratio) == 60,
|
||||
f"Expected 60x sampling ratio, got {sampling_ratio:.1f}x.")
|
||||
|
||||
@@ -2741,7 +2741,7 @@ class MonitoringBudget:
|
||||
datapoints_mo_m = datapoints_mo/MILLION
|
||||
ingestion_cost = datapoints_mo_m * ingestion_cost_per_m
|
||||
storage_q = datapoints_mo * bytes_per_point * byte
|
||||
storage_gb = storage_q.m_as(GB)
|
||||
storage_gb = storage_q.to(GB).magnitude
|
||||
storage_cost = storage_gb * storage_cost_per_gb
|
||||
queries_mo = n_dashboards * n_users * (queries_per_hr * work_hours * work_days)
|
||||
query_cost = queries_mo * query_cost_per
|
||||
@@ -3200,7 +3200,7 @@ Resource justification requires translating technical requirements into business
|
||||
# │ Imports: mlsysim.book (fmt)
|
||||
# │ Exports: current_rate_pct_str, target_rate_pct_str, annual_loss_prevented_str, etc.
|
||||
# └─────────────────────────────────────────────────────────────────────────────
|
||||
from mlsysim.fmt import fmt, check, MarkdownStr
|
||||
from mlsysim.fmt import check, fmt_percent, fmt_percent_range, fmt_pp
|
||||
|
||||
# ┌── LEGO ───────────────────────────────────────────────
|
||||
class FraudDetectionImprovement:
|
||||
@@ -3611,9 +3611,12 @@ class OuraValidationGap:
|
||||
scorer_low_pct_str = fmt_percent(scorer_agreement_low, precision=0, commas=False, style='prose')
|
||||
scorer_high_pct_str = fmt_percent(scorer_agreement_high, precision=0, commas=False, style='prose')
|
||||
gain_points_str = fmt_pp(accuracy_gain * 100, precision=0, commas=False, style="symbol")
|
||||
gap_closed_range_str = MarkdownStr(
|
||||
f"{fmt(gap_closed_high_ceiling * 100, precision=1, commas=False)}-"
|
||||
f"{fmt(gap_closed_low_ceiling * 100, precision=0, commas=False)} percent"
|
||||
gap_closed_range_str = fmt_percent_range(
|
||||
gap_closed_high_ceiling,
|
||||
gap_closed_low_ceiling,
|
||||
precision=(1, 0),
|
||||
commas=False,
|
||||
style="prose",
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -61,17 +61,17 @@ from mlsysim import Models, Platforms
|
||||
from mlsysim.fmt import fmt, fmt_int, fmt_qty, fmt_qty_int, check
|
||||
|
||||
class CompressionDeploymentScale:
|
||||
llm_7b_params = Models.Language.Llama2_7B.parameters.m_as(Bparam)
|
||||
bytes_fp16 = BYTES_FP16.m_as(byte)
|
||||
llm_7b_params = Models.Language.Llama2_7B.parameters.to(Bparam).magnitude
|
||||
bytes_fp16 = BYTES_FP16.to(byte).magnitude
|
||||
|
||||
llm_7b_mem_fp16_gb = llm_7b_params * bytes_fp16
|
||||
llm_7b_mem_fp16 = llm_7b_mem_fp16_gb * GB
|
||||
llm_175b_params = Models.Language.GPT3.parameters.m_as(Bparam)
|
||||
llm_175b_params = Models.Language.GPT3.parameters.to(Bparam).magnitude
|
||||
llm_175b_mem_fp16_gb = llm_175b_params * bytes_fp16
|
||||
llm_175b_mem_fp16 = llm_175b_mem_fp16_gb * GB
|
||||
smartphone_ram_gb = Platforms.Mobile.ram.m_as(GB)
|
||||
smartphone_ram_gb = Platforms.Mobile.ram.to(GB).magnitude
|
||||
smartphone_ram = round(smartphone_ram_gb) * GB
|
||||
mcu_ram_kb = Platforms.Tiny.ram.m_as(KB)
|
||||
mcu_ram_kb = Platforms.Tiny.ram.to(KB).magnitude
|
||||
mcu_ram = mcu_ram_kb * KB
|
||||
|
||||
check(llm_7b_mem_fp16_gb > smartphone_ram_gb,
|
||||
@@ -112,9 +112,9 @@ from mlsysim import Models
|
||||
from mlsysim.fmt import fmt, fmt_qty, check, fmt_count
|
||||
|
||||
class CompressionIronLawQuant:
|
||||
bytes_fp16 = BYTES_FP16.m_as(byte)
|
||||
bytes_int8 = BYTES_INT8.m_as(byte)
|
||||
llm_175b_params = Models.Language.GPT3.parameters.m_as(Bparam)
|
||||
bytes_fp16 = BYTES_FP16.to(byte).magnitude
|
||||
bytes_int8 = BYTES_INT8.to(byte).magnitude
|
||||
llm_175b_params = Models.Language.GPT3.parameters.to(Bparam).magnitude
|
||||
llm_175b_mem_fp16_gb = llm_175b_params * bytes_fp16
|
||||
llm_175b_mem_int8_gb = llm_175b_params * bytes_int8
|
||||
llm_175b_mem_fp16 = llm_175b_mem_fp16_gb * GB
|
||||
@@ -229,17 +229,17 @@ class QuantizationEnergyPhysics:
|
||||
ENERGY_ADD_INT8_PJ = Hardware.Tech.Op.AddInt8.energy
|
||||
|
||||
int8_energy_reduction = 20
|
||||
bytes_fp32 = BYTES_FP32.m_as(byte)
|
||||
bytes_int8 = BYTES_INT8.m_as(byte)
|
||||
bytes_fp32 = BYTES_FP32.to(byte).magnitude
|
||||
bytes_int8 = BYTES_INT8.to(byte).magnitude
|
||||
|
||||
energy_dram = ENERGY_DRAM_ACCESS_PJ.m_as(ureg.picojoule)
|
||||
energy_dram_per_byte = ENERGY_DRAM_PJ_PER_BYTE.m_as(ureg.picojoule/ureg.byte)
|
||||
energy_dram = ENERGY_DRAM_ACCESS_PJ.to(pJ).magnitude
|
||||
energy_dram_per_byte = ENERGY_DRAM_PJ_PER_BYTE.to(pJ / byte).magnitude
|
||||
energy_dram_read_bytes = energy_dram / energy_dram_per_byte
|
||||
energy_dram_read_bits = energy_dram_read_bytes * 8
|
||||
energy_flop_fp32 = ENERGY_FLOP_FP32_PJ.m_as(ureg.picojoule/ureg.count)
|
||||
energy_op_int8 = ENERGY_OP_INT8_PJ.m_as(ureg.picojoule/ureg.count)
|
||||
energy_add_fp32 = ENERGY_ADD_FP32_PJ.m_as(ureg.picojoule)
|
||||
energy_add_int8 = ENERGY_ADD_INT8_PJ.m_as(ureg.picojoule)
|
||||
energy_flop_fp32 = ENERGY_FLOP_FP32_PJ.to(pJ).magnitude
|
||||
energy_op_int8 = ENERGY_OP_INT8_PJ.to(pJ).magnitude
|
||||
energy_add_fp32 = ENERGY_ADD_FP32_PJ.to(pJ).magnitude
|
||||
energy_add_int8 = ENERGY_ADD_INT8_PJ.to(pJ).magnitude
|
||||
energy_float_add_ratio = energy_add_fp32 / energy_add_int8
|
||||
energy_dram_read_ratio = energy_dram / energy_add_int8
|
||||
|
||||
@@ -386,13 +386,13 @@ class QuantizationSpeedup:
|
||||
"""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
params_b = Models.Language.Llama2_7B.parameters.m_as(Bparam)
|
||||
bytes_fp16 = BYTES_FP16.m_as(byte)
|
||||
bytes_int4 = BYTES_INT4.m_as(byte)
|
||||
params_b = Models.Language.Llama2_7B.parameters.to(Bparam).magnitude
|
||||
bytes_fp16 = BYTES_FP16.to(byte).magnitude
|
||||
bytes_int4 = BYTES_INT4.to(byte).magnitude
|
||||
|
||||
device_ram_gb = 16
|
||||
device_ram = device_ram_gb * GB
|
||||
mem_bw_gbs = Hardware.Tech.Storage.SystemMemory.bandwidth.m_as(GB / second)
|
||||
mem_bw_gbs = Hardware.Tech.Storage.SystemMemory.bandwidth.to(GB / second).magnitude
|
||||
mem_bw = Hardware.Tech.Storage.SystemMemory.bandwidth
|
||||
kv_cache_gb = 1.0
|
||||
kv_cache = kv_cache_gb * GB
|
||||
@@ -542,7 +542,7 @@ from mlsysim.fmt import fmt, fmt_int, MarkdownStr, fmt_qty, fmt_qty_int
|
||||
|
||||
def _get_ratio(model_mem, device_mem):
|
||||
"""Return 'ok' if model fits, else 'no (Nx)' with how many times it overflows."""
|
||||
ratio = model_mem.m_as(byte) / device_mem.m_as(byte)
|
||||
ratio = model_mem.to(byte).magnitude / device_mem.to(byte).magnitude
|
||||
if ratio < 1:
|
||||
return "ok"
|
||||
return f"no ({ratio:.0f}x)"
|
||||
@@ -572,7 +572,7 @@ class ModelDeviceComparison:
|
||||
resnet_tiny = _get_ratio(resnet_mem, tiny_mem)
|
||||
mobilenet_tiny = _get_ratio(mobilenet_mem, tiny_mem)
|
||||
mobilenet_int8_tiny = _get_ratio(mobilenet_int8_mem, tiny_mem)
|
||||
mobilenet_int8_tiny_ratio = mobilenet_int8_mem.m_as(byte) / tiny_mem.m_as(byte)
|
||||
mobilenet_int8_tiny_ratio = mobilenet_int8_mem.to(byte).magnitude / tiny_mem.to(byte).magnitude
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ──────────────────────────────────────────
|
||||
# DS-CNN always fits TinyML—sanity check
|
||||
@@ -2372,12 +2372,12 @@ class LowRankFactorization:
|
||||
# Step 1: Full Matrix: N * N
|
||||
full_params = mat_dim * mat_dim
|
||||
full = full_params * bytes_per_param * byte
|
||||
full_mb = full.m_as(MB)
|
||||
full_mb = full.to(MB).magnitude
|
||||
|
||||
# Step 2: Factored: 2 * N * K
|
||||
factored_params = 2 * mat_dim * rank_k
|
||||
factored = factored_params * bytes_per_param * byte
|
||||
factored_mb = factored.m_as(MB)
|
||||
factored_mb = factored.to(MB).magnitude
|
||||
|
||||
data_reduction = full_mb/factored_mb
|
||||
|
||||
@@ -2996,9 +2996,9 @@ class QuantizationMemoryAnchor:
|
||||
"""Local 7-billion-parameter FP16 memory anchor for quantization motivation."""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
llm_7b_params_b = Models.Language.Llama2_7B.parameters.m_as(Bparam)
|
||||
llm_7b_params_b = Models.Language.Llama2_7B.parameters.to(Bparam).magnitude
|
||||
bytes_fp16 = 2
|
||||
smartphone_ram_gb = Platforms.Mobile.ram.m_as(GB)
|
||||
smartphone_ram_gb = Platforms.Mobile.ram.to(GB).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
llm_7b_mem_fp16_gb = llm_7b_params_b * bytes_fp16
|
||||
@@ -3070,10 +3070,10 @@ class EnergyCosts:
|
||||
ENERGY_ADD_INT32_PJ = Hardware.Tech.Op.AddInt32.energy
|
||||
ENERGY_ADD_INT8_PJ = Hardware.Tech.Op.AddInt8.energy
|
||||
|
||||
energy_add_fp32_pj = ENERGY_ADD_FP32_PJ.m_as(ureg.picojoule)
|
||||
energy_add_fp16_pj = ENERGY_ADD_FP16_PJ.m_as(ureg.picojoule)
|
||||
energy_add_int32_pj = ENERGY_ADD_INT32_PJ.m_as(ureg.picojoule)
|
||||
energy_add_int8_pj = ENERGY_ADD_INT8_PJ.m_as(ureg.picojoule)
|
||||
energy_add_fp32_pj = ENERGY_ADD_FP32_PJ.to(pJ).magnitude
|
||||
energy_add_fp16_pj = ENERGY_ADD_FP16_PJ.to(pJ).magnitude
|
||||
energy_add_int32_pj = ENERGY_ADD_INT32_PJ.to(pJ).magnitude
|
||||
energy_add_int8_pj = ENERGY_ADD_INT8_PJ.to(pJ).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE—(values are direct from constants) ─────────────────
|
||||
|
||||
@@ -3208,12 +3208,12 @@ class EnergyDividend:
|
||||
ENERGY_DRAM_PJ_PER_BYTE = Hardware.Tech.Memory.DRAM.energy_per_byte
|
||||
ENERGY_OP_INT8_PJ = Hardware.Tech.Op.OpInt8.energy
|
||||
|
||||
e_add_fp32 = ENERGY_ADD_FP32_PJ.m_as('pJ')
|
||||
e_add_int8 = ENERGY_ADD_INT8_PJ.m_as('pJ')
|
||||
e_dram = ENERGY_DRAM_ACCESS_PJ.m_as(ureg.picojoule)
|
||||
e_dram_per_byte = ENERGY_DRAM_PJ_PER_BYTE.m_as(ureg.picojoule/ureg.byte)
|
||||
e_add_fp32 = ENERGY_ADD_FP32_PJ.to(pJ).magnitude
|
||||
e_add_int8 = ENERGY_ADD_INT8_PJ.to(pJ).magnitude
|
||||
e_dram = ENERGY_DRAM_ACCESS_PJ.to(pJ).magnitude
|
||||
e_dram_per_byte = ENERGY_DRAM_PJ_PER_BYTE.to(pJ / byte).magnitude
|
||||
dram_read_bits = (e_dram / e_dram_per_byte) * 8
|
||||
e_int8_mac = ENERGY_OP_INT8_PJ.m_as(ureg.picojoule/ureg.count) + e_add_int8
|
||||
e_int8_mac = ENERGY_OP_INT8_PJ.to(pJ).magnitude + e_add_int8
|
||||
|
||||
dividend = e_add_fp32 / e_add_int8
|
||||
dram_read_vs_int8_mac = e_dram / e_int8_mac
|
||||
@@ -3382,7 +3382,7 @@ To make these gains concrete, consider the quantization savings when deploying a
|
||||
# │ fp16_size_gb_str, int4_size_gb_str, compression_ratio_str
|
||||
# └─────────────────────────────────────────────────────────────────────────────
|
||||
from mlsysim import Models
|
||||
from mlsysim.fmt import fmt_int, fmt, fmt_qty, fmt_qty_int, check, MarkdownStr
|
||||
from mlsysim.fmt import fmt_int, fmt, fmt_qty, fmt_qty_int, check
|
||||
|
||||
# ┌── LEGO ───────────────────────────────────────────────
|
||||
class QuantizationSavings:
|
||||
@@ -3392,11 +3392,11 @@ class QuantizationSavings:
|
||||
"""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ───────────────────────────────────────────────
|
||||
params_b = round(Models.Language.Llama3_8B.parameters.m_as(Bparam))
|
||||
params_b = round(Models.Language.Llama3_8B.parameters.to(Bparam).magnitude)
|
||||
gpu_mem_fp16_gb = 24
|
||||
gpu_mem_int4_gb = 8
|
||||
bytes_fp16 = BYTES_FP16.m_as(byte)
|
||||
bytes_int4 = BYTES_INT4.m_as(byte)
|
||||
bytes_fp16 = BYTES_FP16.to(byte).magnitude
|
||||
bytes_int4 = BYTES_INT4.to(byte).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
fp16_size_gb = params_b * bytes_fp16
|
||||
@@ -3410,7 +3410,7 @@ class QuantizationSavings:
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
check(ratio == 4.0, f"FP16/INT4 ratio should be exactly 4.0, got {ratio}")
|
||||
check(BYTES_FP32.m_as(ureg.byte) / BYTES_INT8.m_as(ureg.byte) == 4.0, "Narrative Violation: INT8 compression ratio is no longer exactly 4x! Got ratio.")
|
||||
check(BYTES_FP32.to(byte).magnitude / BYTES_INT8.to(byte).magnitude == 4.0, "Narrative Violation: INT8 compression ratio is no longer exactly 4x! Got ratio.")
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ──────────────────────────────────────────────
|
||||
llm_params_b_str = fmt(params_b, precision=0, commas=False)
|
||||
@@ -3419,8 +3419,8 @@ class QuantizationSavings:
|
||||
fp16_size_gb_str = fmt_qty_int(fp16_size, GB, commas=False)
|
||||
int4_size_gb_str = fmt_qty_int(int4_size, GB, commas=False)
|
||||
compression_ratio_str = fmt_int(ratio, commas=False)
|
||||
gpu_mem_fp16_gb_str = MarkdownStr(f"{fmt_qty(gpu_mem_fp16, GB, precision=0, commas=False)} GPU")
|
||||
gpu_mem_int4_gb_str = MarkdownStr(f"{fmt_qty(gpu_mem_int4, GB, precision=0, commas=False)} GPU")
|
||||
gpu_mem_fp16_gb_str = fmt_qty(gpu_mem_fp16, GB, precision=0, commas=False)
|
||||
gpu_mem_int4_gb_str = fmt_qty(gpu_mem_int4, GB, precision=0, commas=False)
|
||||
```
|
||||
|
||||
::: {#nbk-model-compression-quantization-savings .callout-notebook title="Quantization savings"}
|
||||
@@ -3430,12 +3430,12 @@ class QuantizationSavings:
|
||||
**FP16 (Half Precision)**
|
||||
|
||||
- **Size**: `{python} QuantizationSavings.llm_params_b_str` $\times 10^9 \times$ `{python} QuantizationSavings.fp16_bytes_str` bytes (16-bit) = `{python} QuantizationSavings.fp16_size_gb_str`
|
||||
- **Hardware Req**: Requires `{python} QuantizationSavings.gpu_mem_fp16_gb_str` (for example, A10G, 3090, 4090).
|
||||
- **Hardware Req**: Requires `{python} QuantizationSavings.gpu_mem_fp16_gb_str` GPU (for example, A10G, 3090, 4090).
|
||||
|
||||
**INT4 (4-bit Quantization)**
|
||||
|
||||
- **Size**: `{python} QuantizationSavings.llm_params_b_str` $\times 10^9 \times$ `{python} QuantizationSavings.int4_bytes_str` bytes (INT4) = `{python} QuantizationSavings.int4_size_gb_str`
|
||||
- **Hardware Req**: Fits comfortably on `{python} QuantizationSavings.gpu_mem_int4_gb_str` (for example, T4, consumer laptops).
|
||||
- **Hardware Req**: Fits comfortably on `{python} QuantizationSavings.gpu_mem_int4_gb_str` GPU (for example, T4, consumer laptops).
|
||||
|
||||
**Impact**: `{python} QuantizationSavings.compression_ratio_str`$\times$ compression allows deployment on commodity hardware instead of requiring a larger accelerator primarily for weight storage.
|
||||
|
||||
@@ -3653,8 +3653,8 @@ class A100Int8Speedup:
|
||||
"""A100 Tensor Core throughput: FP16 vs INT8."""
|
||||
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
a100_tflops_fp16 = Hardware.Cloud.A100.compute.peak_flops.m_as(TFLOPs/second)
|
||||
a100_tops_int8 = Hardware.Cloud.A100.compute.precision_flops["int8"].m_as(TOPS)
|
||||
a100_tflops_fp16 = Hardware.Cloud.A100.compute.peak_flops.to(TFLOPs/second).magnitude
|
||||
a100_tops_int8 = Hardware.Cloud.A100.compute.precision_flops["int8"].to(TOPS).magnitude
|
||||
a100_fp16 = Hardware.Cloud.A100.compute.peak_flops
|
||||
a100_int8 = Hardware.Cloud.A100.compute.precision_flops["int8"]
|
||||
bandwidth_bound_speedup = 2 # FP16 -> INT8 memory-traffic-dominated case
|
||||
@@ -3792,7 +3792,7 @@ from mlsysim.core.constants import Bparam
|
||||
from mlsysim.fmt import fmt
|
||||
|
||||
class QuantizationSavingsRecap:
|
||||
params_b = round(Models.Language.Llama3_8B.parameters.m_as(Bparam))
|
||||
params_b = round(Models.Language.Llama3_8B.parameters.to(Bparam).magnitude)
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
llm_params_b_str = fmt(params_b, precision=0, commas=False)
|
||||
```
|
||||
@@ -3830,10 +3830,10 @@ class EnergyRatio:
|
||||
ENERGY_ADD_FP32_PJ = Hardware.Tech.Op.AddFp32.energy
|
||||
ENERGY_ADD_INT8_PJ = Hardware.Tech.Op.AddInt8.energy
|
||||
|
||||
energy_mul_fp32_pj = ENERGY_FLOP_FP32_PJ.m_as(ureg.picojoule/ureg.count)
|
||||
energy_add_fp32_pj = ENERGY_ADD_FP32_PJ.m_as(ureg.picojoule)
|
||||
energy_op_int8_pj = ENERGY_OP_INT8_PJ.m_as(ureg.picojoule/ureg.count)
|
||||
energy_add_int8_pj = ENERGY_ADD_INT8_PJ.m_as(ureg.picojoule)
|
||||
energy_mul_fp32_pj = ENERGY_FLOP_FP32_PJ.to(pJ).magnitude
|
||||
energy_add_fp32_pj = ENERGY_ADD_FP32_PJ.to(pJ).magnitude
|
||||
energy_op_int8_pj = ENERGY_OP_INT8_PJ.to(pJ).magnitude
|
||||
energy_add_int8_pj = ENERGY_ADD_INT8_PJ.to(pJ).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
fp32_mac_pj = energy_mul_fp32_pj + energy_add_fp32_pj # ~4.6 pJ
|
||||
@@ -5274,7 +5274,7 @@ class FusionCalc:
|
||||
kernels_unfused = 159
|
||||
kernels_fused = 53
|
||||
latency_per_kernel_us = 10
|
||||
v100_bw_gbs = Hardware.Cloud.V100.memory.bandwidth.m_as(GB/second)
|
||||
v100_bw_gbs = Hardware.Cloud.V100.memory.bandwidth.to(GB/second).magnitude
|
||||
|
||||
feat_map_mb = conv_channels * conv_spatial * conv_spatial * bytes_per_element/MILLION
|
||||
feat_map = feat_map_mb * MB
|
||||
@@ -7390,16 +7390,16 @@ class ResNet50Int8Metrics:
|
||||
p_int8 = Engine.solve(m, h, batch_size=1, precision="int8")
|
||||
warnings.filters[:] = _prev_filters
|
||||
|
||||
fp32_latency_ms = p_fp32.latency.m_as("ms")
|
||||
int8_latency_ms = p_int8.latency.m_as("ms")
|
||||
fp32_latency_ms = p_fp32.latency.to(ms).magnitude
|
||||
int8_latency_ms = p_int8.latency.to(ms).magnitude
|
||||
fp32_size = p_fp32.memory_footprint
|
||||
int8_size = p_int8.memory_footprint
|
||||
fp32_size_mb = fp32_size.m_as("MB")
|
||||
int8_size_mb = int8_size.m_as("MB")
|
||||
fp32_size_mb = fp32_size.to(MB).magnitude
|
||||
int8_size_mb = int8_size.to(MB).magnitude
|
||||
fp32_energy = p_fp32.energy
|
||||
int8_energy = p_int8.energy
|
||||
fp32_energy_j = fp32_energy.m_as("J")
|
||||
int8_energy_j = int8_energy.m_as("J")
|
||||
fp32_energy_j = fp32_energy.to(joule).magnitude
|
||||
int8_energy_j = int8_energy.to(joule).magnitude
|
||||
|
||||
top1_drop = fp32_top1 - int8_top1
|
||||
latency_speedup = fp32_latency_ms / int8_latency_ms
|
||||
|
||||
@@ -761,7 +761,7 @@ class StaticBatchCalc:
|
||||
dynamic_latency_budget_ms_value = 100 # real-time latency budget (ms)
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
batch_total_s_value = (n_photos_value * inference_ms_value * ms).m_as(second)
|
||||
batch_total_s_value = (n_photos_value * inference_ms_value * ms).to(second).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
n_photos_str = fmt_count(n_photos_value, label="photo")
|
||||
@@ -944,7 +944,7 @@ class ResNetServingSpectrum:
|
||||
cloud_inf_b16_ms = 14.0
|
||||
cloud_throughput = 1143
|
||||
cloud_vram = 2 * GB
|
||||
cloud_vram_gb = cloud_vram.m_as(GB)
|
||||
cloud_vram_gb = cloud_vram.to(GB).magnitude
|
||||
|
||||
# Mobile (Smartphone) Performance
|
||||
mobile_inf_npu_ms = 12.0
|
||||
@@ -952,13 +952,13 @@ class ResNetServingSpectrum:
|
||||
mobile_throughput = 80
|
||||
mobile_energy_npu = 0.8 * ureg.millijoule
|
||||
mobile_energy_cpu = 4.2 * ureg.millijoule
|
||||
mobile_energy_npu_mj = mobile_energy_npu.m_as(ureg.millijoule)
|
||||
mobile_energy_cpu_mj = mobile_energy_cpu.m_as(ureg.millijoule)
|
||||
mobile_energy_npu_mj = mobile_energy_npu.to(mJ).magnitude
|
||||
mobile_energy_cpu_mj = mobile_energy_cpu.to(mJ).magnitude
|
||||
|
||||
# TinyML (Cortex-M7) Performance
|
||||
tiny_inf_ms = 120.0
|
||||
tiny_energy = 12.0 * ureg.millijoule
|
||||
tiny_energy_mj = tiny_energy.m_as(ureg.millijoule)
|
||||
tiny_energy_mj = tiny_energy.to(mJ).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
# Step 1: Calculate sizes using the Digital Twins
|
||||
@@ -966,13 +966,13 @@ class ResNetServingSpectrum:
|
||||
mobile_size = m_resnet.size_in_bytes(BYTES_INT8)
|
||||
tiny_original = m_resnet.size_in_bytes(BYTES_INT8)
|
||||
tiny_alt = m_mobilenet.size_in_bytes(BYTES_INT8)
|
||||
cloud_size_mb = cloud_size.m_as('MB')
|
||||
mobile_size_mb = mobile_size.m_as('MB')
|
||||
tiny_original_mb = tiny_original.m_as('MB')
|
||||
tiny_alt_mb = tiny_alt.m_as('MB')
|
||||
cloud_size_mb = cloud_size.to(MB).magnitude
|
||||
mobile_size_mb = mobile_size.to(MB).magnitude
|
||||
tiny_original_mb = tiny_original.to(MB).magnitude
|
||||
tiny_alt_mb = tiny_alt.to(MB).magnitude
|
||||
|
||||
# Step 2: TinyML feasibility check
|
||||
tiny_limit_mb = s_tiny.ram.m_as('MB')
|
||||
tiny_limit_mb = s_tiny.ram.to(MB).magnitude
|
||||
tiny_feasibility = tiny_original_mb < tiny_limit_mb
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
@@ -2146,7 +2146,7 @@ class CapacityPlanningAnchor:
|
||||
"""
|
||||
qps_target = 1000
|
||||
slo_ms = 50
|
||||
concurrency_slots = int(qps_target * (slo_ms * ms).m_as(second))
|
||||
concurrency_slots = int(qps_target * (slo_ms * ms).to(second).magnitude)
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
qps_str = fmt_rate(qps_target, "QPS", commas=False)
|
||||
@@ -2266,12 +2266,12 @@ class BatchingTax:
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ─────────────────────────────────────────
|
||||
# Step 1: Batch 1
|
||||
w_form_b1 = ((1-1) / (2 * lambda_qps) * second).m_as(ms) # 0 ms
|
||||
w_form_b1 = ((1-1) / (2 * lambda_qps) * second).to(ms).magnitude # 0 ms
|
||||
lat_b1 = w_form_b1 + t_inf_b1
|
||||
|
||||
# Batch 32
|
||||
# Step 2: Formation Delay ~ (B-1) / (2 * lambda)
|
||||
w_form_b32 = ((32-1) / (2 * lambda_qps) * second).m_as(ms) # ~31 ms
|
||||
w_form_b32 = ((32-1) / (2 * lambda_qps) * second).to(ms).magnitude # ~31 ms
|
||||
lat_b32 = w_form_b32 + t_inf_b32
|
||||
|
||||
penalty_ratio = lat_b32 / lat_b1
|
||||
@@ -2711,12 +2711,12 @@ class ModelSwapCalc:
|
||||
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
model_size = 10 * GB # model size
|
||||
model_size_gb_value = model_size.m_as(GB)
|
||||
model_size_gb_value = model_size.to(GB).magnitude
|
||||
pcie_bw = Hardware.Cloud.A100.interconnect.bandwidth # PCIe Gen4 x16 bandwidth
|
||||
pcie_bw_gbs_value = pcie_bw.m_as(GB/second)
|
||||
pcie_bw_gbs_value = pcie_bw.to(GB/second).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
model_swap_ms_value = (model_size_gb_value / pcie_bw_gbs_value * second).m_as(ms)
|
||||
model_swap_ms_value = (model_size_gb_value / pcie_bw_gbs_value * second).to(ms).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
model_size_gb_str = fmt_qty(model_size, GB, precision=0, commas=False)
|
||||
@@ -3037,7 +3037,7 @@ def total_latency_value(b, _T=10.0, _fixed=5.0, _per_image=0.6):
|
||||
return _T + _fixed + _per_image * b
|
||||
|
||||
def throughput_value(b, _T=10.0, _fixed=5.0, _per_image=0.6):
|
||||
return b / ((_T + _fixed + _per_image * b) * ms).m_as(second)
|
||||
return b / ((_T + _fixed + _per_image * b) * ms).to(second).magnitude
|
||||
|
||||
class BatchingAnalysisCalc:
|
||||
"""Quantifies batching efficiency: batch-32 achieves 14.6× throughput gain over batch-1."""
|
||||
@@ -3260,7 +3260,7 @@ class SloViolationCalc:
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
# service_time_value is a module-level function from batching-analysis-calc
|
||||
mean_batch_value = qps_value * (T_slo_value * ms).m_as(second)
|
||||
mean_batch_value = qps_value * (T_slo_value * ms).to(second).magnitude
|
||||
mean_wait_value = T_slo_value / 2
|
||||
mean_service_value = service_time_value(int(mean_batch_value))
|
||||
mean_latency_value = mean_wait_value + mean_service_value
|
||||
@@ -3407,7 +3407,7 @@ class PracticalConfigCalc:
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
pc_batch_budget_ms_value = pc_slo_ms_value * pc_budget_pct_value
|
||||
pc_max_window_ms_value = pc_batch_budget_ms_value
|
||||
pc_window_seconds_value = (pc_config_window_ms_value * ms).m_as(second)
|
||||
pc_window_seconds_value = (pc_config_window_ms_value * ms).to(second).magnitude
|
||||
pc_expected_batch_value = pc_qps_value * pc_window_seconds_value
|
||||
|
||||
poisson_term = math.exp(-pc_expected_batch_value)
|
||||
@@ -3424,7 +3424,7 @@ class PracticalConfigCalc:
|
||||
|
||||
pc_peak_service_ms_value = pc_base_service_ms_value + pc_per_image_ms_value * pc_config_batch_value
|
||||
pc_predicted_throughput_value = (
|
||||
pc_config_batch_value / (pc_peak_service_ms_value * ms).m_as(second) * pc_throughput_efficiency_value
|
||||
pc_config_batch_value / (pc_peak_service_ms_value * ms).to(second).magnitude * pc_throughput_efficiency_value
|
||||
)
|
||||
|
||||
check(pc_predicted_p99_ms_value < pc_slo_ms_value, "Predicted p99 should stay within the serving SLO.")
|
||||
@@ -3563,13 +3563,13 @@ class TrafficAdaptiveBatchingCalc:
|
||||
arrival_rates = (100, 500, 1_000, 5_000)
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
service_time_s = (service_time_ms * ms).m_as(second)
|
||||
latency_budget_s = ((latency_slo_ms - service_time_ms) * ms).m_as(second)
|
||||
service_time_s = (service_time_ms * ms).to(second).magnitude
|
||||
latency_budget_s = ((latency_slo_ms - service_time_ms) * ms).to(second).magnitude
|
||||
rows = []
|
||||
for qps in arrival_rates:
|
||||
window_s = min(latency_budget_s, math.sqrt(service_time_s / qps))
|
||||
avg_batch = qps * window_s
|
||||
approx_latency_ms = service_time_ms + (window_s / (1 * ms).m_as(second))
|
||||
approx_latency_ms = service_time_ms + (window_s / (1 * ms).to(second).magnitude)
|
||||
rows.append((qps, window_s, avg_batch, approx_latency_ms))
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
@@ -3584,22 +3584,22 @@ class TrafficAdaptiveBatchingCalc:
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
qps_100_str = fmt_rate(rows[0][0], "QPS")
|
||||
window_100_ms_str = fmt_time(rows[0][1] / (1 * ms).m_as(second), 'millisecond', precision=1, commas=False)
|
||||
window_100_ms_str = fmt_time(rows[0][1] / (1 * ms).to(second).magnitude, 'millisecond', precision=1, commas=False)
|
||||
avg_batch_100_str = fmt(rows[0][2], precision=1, commas=False)
|
||||
latency_100_ms_str = fmt_time(rows[0][3], 'millisecond', precision=1, commas=False)
|
||||
|
||||
qps_500_str = fmt_rate(rows[1][0], "QPS")
|
||||
window_500_ms_str = fmt_time(rows[1][1] / (1 * ms).m_as(second), 'millisecond', precision=1, commas=False)
|
||||
window_500_ms_str = fmt_time(rows[1][1] / (1 * ms).to(second).magnitude, 'millisecond', precision=1, commas=False)
|
||||
avg_batch_500_str = fmt(rows[1][2], precision=1, commas=False)
|
||||
latency_500_ms_str = fmt_time(rows[1][3], 'millisecond', precision=1, commas=False)
|
||||
|
||||
qps_1000_str = fmt_rate(rows[2][0], "QPS")
|
||||
window_1000_ms_str = fmt_time(rows[2][1] / (1 * ms).m_as(second), 'millisecond', precision=0, commas=False)
|
||||
window_1000_ms_str = fmt_time(rows[2][1] / (1 * ms).to(second).magnitude, 'millisecond', precision=0, commas=False)
|
||||
avg_batch_1000_str = fmt(rows[2][2], precision=0, commas=False)
|
||||
latency_1000_ms_str = fmt_time(rows[2][3], 'millisecond', precision=0, commas=False)
|
||||
|
||||
qps_5000_str = fmt_rate(rows[3][0], "QPS")
|
||||
window_5000_ms_str = fmt_time(rows[3][1] / (1 * ms).m_as(second), 'millisecond', precision=1, commas=False)
|
||||
window_5000_ms_str = fmt_time(rows[3][1] / (1 * ms).to(second).magnitude, 'millisecond', precision=1, commas=False)
|
||||
avg_batch_5000_str = fmt(rows[3][2], precision=1, commas=False)
|
||||
latency_5000_ms_str = fmt_time(rows[3][3], 'millisecond', precision=1, commas=False)
|
||||
```
|
||||
@@ -3714,7 +3714,7 @@ class MobileServingCalc:
|
||||
"mobile pipeline sustained power should be ~60–70 mW",
|
||||
)
|
||||
inferences_per_battery_m = (
|
||||
(battery_wh / m_total_mj).m_as(ureg.count) / MILLION
|
||||
(battery_wh / m_total_mj).to(count).magnitude / MILLION
|
||||
)
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -3902,7 +3902,7 @@ class CarbonCostH100Tdp:
|
||||
h_h100 = Hardware.Cloud.H100
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
h100_tdp_value = h_h100.tdp.m_as(watt)
|
||||
h100_tdp_value = h_h100.tdp.to(watt).magnitude
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
h100_tdp_str = fmt(h100_tdp_value, precision=0, commas=False) # e.g. "700" W
|
||||
@@ -3953,14 +3953,14 @@ class CarbonCostCalc:
|
||||
cc_low_util_joules = cc_idle_power / cc_low_util_tokens_sec
|
||||
|
||||
check(
|
||||
cc_total_tokens_sec.m_as(count / second) == 912,
|
||||
f"114 concurrent requests × 8 tokens/s should be 912 tokens/s, got {cc_total_tokens_sec.m_as(count / second):.0f}",
|
||||
cc_total_tokens_sec.to(count / second).magnitude == 912,
|
||||
f"114 concurrent requests × 8 tokens/s should be 912 tokens/s, got {cc_total_tokens_sec.to(count / second).magnitude:.0f}",
|
||||
)
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
cc_concurrent_str = fmt(cc_concurrent_req_value, precision=0, commas=False)
|
||||
cc_tokens_req_str = fmt_rate(cc_tokens_per_sec_req_value, 'tokens/s', precision=0, commas=False)
|
||||
cc_total_tokens_str = fmt_rate(round(cc_total_tokens_sec.m_as(count / second)), 'tokens/s', precision=0, commas=False)
|
||||
cc_total_tokens_str = fmt_rate(round(cc_total_tokens_sec.to(count / second).magnitude), 'tokens/s', precision=0, commas=False)
|
||||
cc_host_overhead_str = fmt_qty(cc_host_overhead, watt, precision=0, commas=False)
|
||||
cc_total_power_str = fmt_qty(cc_total_power, watt, precision=0, commas=False)
|
||||
cc_joules_token_str = fmt_qty(cc_joules_per_token, joule, precision=2, commas=False, per='token')
|
||||
@@ -4202,16 +4202,16 @@ class PrecisionTradeoffCalc:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
pt_fp32_ms_value = 2.8
|
||||
pt_fp32_mem = 98 * MB
|
||||
pt_fp32_mem_mb_value = pt_fp32_mem.m_as(MB)
|
||||
pt_fp32_mem_mb_value = pt_fp32_mem.to(MB).magnitude
|
||||
pt_fp32_acc_value = 76.13
|
||||
pt_fp16_ms_value = 1.4
|
||||
pt_fp16_mem = 49 * MB
|
||||
pt_fp16_mem_mb_value = pt_fp16_mem.m_as(MB)
|
||||
pt_fp16_mem_mb_value = pt_fp16_mem.to(MB).magnitude
|
||||
pt_fp16_acc_value = 76.13
|
||||
pt_fp16_util_value = 85
|
||||
pt_int8_ms_value = 0.9
|
||||
pt_int8_mem = 25 * MB
|
||||
pt_int8_mem_mb_value = pt_int8_mem.m_as(MB)
|
||||
pt_int8_mem_mb_value = pt_int8_mem.to(MB).magnitude
|
||||
pt_int8_ptq_acc_value = 75.80
|
||||
pt_int8_qat_acc_value = 76.05
|
||||
pt_int8_util_value = 92
|
||||
@@ -4578,13 +4578,13 @@ fig, ax, COLORS, plt = viz.setup_plot(figsize=(10, 5))
|
||||
|
||||
# ┌── 2. ARRAYS ────────────────────────────────────────────────────────────────
|
||||
seq_len = np.linspace(0, 32000, 100)
|
||||
layers, d_model, bytes_per_param = 80, 8192, BYTES_FP16.m_as(byte) # 70-billion-parameter model, FP16
|
||||
layers, d_model, bytes_per_param = 80, 8192, BYTES_FP16.to(byte).magnitude # 70-billion-parameter model, FP16
|
||||
gqa_ratio = 8 # Grouped Query Attention (8x reduction)
|
||||
|
||||
def get_kv_gb(batch, seq):
|
||||
# KV cache size = 2 * layers * d_model * seq * batch * bytes_per_param/gqa_ratio
|
||||
bytes_total = (2 * layers * d_model * seq * batch * bytes_per_param) / gqa_ratio
|
||||
return (bytes_total * byte).m_as(GB)
|
||||
return (bytes_total * byte).to(GB).magnitude
|
||||
|
||||
batches = [1, 4, 16, 32]
|
||||
colors = [COLORS['BlueLine'], COLORS['GreenLine'], COLORS['OrangeLine'], COLORS['VioletLine']]
|
||||
@@ -4666,9 +4666,9 @@ class LlmCaseStudyHwSpecs:
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
a100_bw = h_a100.memory.bandwidth
|
||||
a100_bw_tbs_value = a100_bw.m_as(TB/second)
|
||||
a100_bw_gbs_value = a100_bw.m_as(GB/second)
|
||||
h100_mem_value = h_h100.memory.capacity.m_as(GiB)
|
||||
a100_bw_tbs_value = a100_bw.to(TB/second).magnitude
|
||||
a100_bw_gbs_value = a100_bw.to(GB/second).magnitude
|
||||
h100_mem_value = h_h100.memory.capacity.to(GiB).magnitude
|
||||
_serving = ServingModel().solve(
|
||||
m, h_a100,
|
||||
seq_len=prompt_tokens + decode_tokens,
|
||||
@@ -4677,7 +4677,7 @@ class LlmCaseStudyHwSpecs:
|
||||
efficiency=1.0,
|
||||
)
|
||||
case_study_model_weight = _serving.total_memory_required
|
||||
case_study_model_weight_gb = case_study_model_weight.m_as(GB)
|
||||
case_study_model_weight_gb = case_study_model_weight.to(GB).magnitude
|
||||
a100_token_ms_value = case_study_model_weight_gb / a100_bw_gbs_value * THOUSAND
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -4807,11 +4807,11 @@ class LlmServingCalc:
|
||||
efficiency=0.4 # realized efficiency
|
||||
)
|
||||
|
||||
model_weight_gb_value = res.total_memory_required.m_as(GB)
|
||||
model_weight_gb_value = res.total_memory_required.to(GB).magnitude
|
||||
realized_tpot = res.itl
|
||||
realized_tpot_ms_value = realized_tpot.m_as(ms)
|
||||
realized_tpot_ms_value = realized_tpot.to(ms).magnitude
|
||||
|
||||
_h100_bw_gbs = h.memory.bandwidth.m_as(GB/second)
|
||||
_h100_bw_gbs = h.memory.bandwidth.to(GB/second).magnitude
|
||||
|
||||
# Theoretical floor (100% BW efficiency)
|
||||
token_time_theoretical_ms_value = (model_weight_gb_value / _h100_bw_gbs * THOUSAND)
|
||||
@@ -4822,12 +4822,12 @@ class LlmServingCalc:
|
||||
# KV Cache Sizing using library results
|
||||
kv_cache_reserved = 72 * GB
|
||||
kv_per_token = m.get_kv_cache_size(seq_len=1, batch_size=1, precision=BYTES_INT4)
|
||||
kv_capacity_tokens_value = int(round((kv_cache_reserved / kv_per_token).m_as(count)))
|
||||
kv_cache_gb_value = kv_cache_reserved.m_as(GB)
|
||||
kv_capacity_tokens_value = int(round((kv_cache_reserved / kv_per_token).to(count).magnitude))
|
||||
kv_cache_gb_value = kv_cache_reserved.to(GB).magnitude
|
||||
|
||||
concurrent_batch_value = int(kv_capacity_tokens_value / total_tokens)
|
||||
req_time_s = res.ttft + total_decode_s
|
||||
req_time_s_value = req_time_s.m_as(second)
|
||||
req_time_s_value = req_time_s.to(second).magnitude
|
||||
|
||||
# Throughput scaling
|
||||
prefill_tokens_per_s_value = 10_000 # H100 prefill rate
|
||||
@@ -4844,7 +4844,7 @@ class LlmServingCalc:
|
||||
remaining_vram = remaining_vram_gb_value * GB
|
||||
|
||||
check(
|
||||
abs(decode_tokens_value * realized_tpot_ms_value / 1000 - total_decode_s.m_as(second)) < 1e-6,
|
||||
abs(decode_tokens_value * realized_tpot_ms_value / 1000 - total_decode_s.to(second).magnitude) < 1e-6,
|
||||
"Decode latency must equal tokens × TPOT",
|
||||
)
|
||||
check(
|
||||
|
||||
@@ -5017,7 +5017,7 @@ class ThroughputCeilingCalc:
|
||||
effective_tflops_high = midrange_gpu_tflops * 0.60 # 60% utilization
|
||||
headroom = effective_tflops_low * THOUSAND / sustained_gflops
|
||||
|
||||
objdet_sustained_tflops = (fps * objdet_gflops * GFLOPs / second).to(TFLOPs).magnitude
|
||||
objdet_sustained_tflops = (fps * objdet_gflops * GFLOPs / second).to(TFLOPs / second).magnitude
|
||||
objdet_headroom = effective_tflops_low/objdet_sustained_tflops
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ──────────────────────────────────────────
|
||||
|
||||
@@ -1525,7 +1525,7 @@ Acknowledging that efficiency matters is the easy part; the harder engineering c
|
||||
```{python}
|
||||
#| label: edge-efficiency-calc
|
||||
#| echo: false
|
||||
from mlsysim.core.constants import watt, milliwatt, second, ms, Mparam, Kparam
|
||||
from mlsysim.core.constants import watt, milliwatt, second, ms, Mparam, Kparam, param
|
||||
from mlsysim.fmt import fmt, check, MarkdownStr, fmt_qty, fmt_count, fmt_time
|
||||
from mlsysim import Hardware, Models
|
||||
|
||||
@@ -1568,8 +1568,8 @@ class EdgeEfficiencyCalc:
|
||||
tiny_fits_phone = tiny_power <= smart_power and tiny_latency <= smart_latency
|
||||
tiny_fits_iot = tiny_power <= iot_power and tiny_latency <= iot_latency
|
||||
|
||||
tiny_wear_power_margin = (wear_power / tiny_power).m_as("")
|
||||
mv2_wear_power_overage = (mv2_power / wear_power).m_as("")
|
||||
tiny_wear_power_margin = (wear_power / tiny_power).to("").magnitude
|
||||
mv2_wear_power_overage = (mv2_power / wear_power).to("").magnitude
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
check(tiny_fits_iot, "TinyML should fit the IoT power and latency envelope.")
|
||||
@@ -1581,25 +1581,25 @@ class EdgeEfficiencyCalc:
|
||||
smart_power_str = fmt_qty(smart_power, watt, precision=0, commas=False)
|
||||
smart_latency_str = fmt_qty(smart_latency, ms, precision=0, commas=False)
|
||||
iot_power_str = fmt_qty(iot_power, milliwatt, precision=0, commas=False)
|
||||
iot_latency_str = fmt_time(iot_latency.m_as(second), 'second', precision=0, commas=False, style='word')
|
||||
iot_latency_str = fmt_time(iot_latency.to(second).magnitude, 'second', precision=0, commas=False, style='word')
|
||||
cam_power_str = fmt_qty(cam_power, watt, precision=0, commas=False)
|
||||
cam_latency_str = fmt_qty(cam_latency, ms, precision=0, commas=False)
|
||||
wear_power_str = fmt_qty(wear_power, milliwatt, precision=0, commas=False)
|
||||
wear_latency_str = fmt_qty(wear_latency, ms, precision=0, commas=False)
|
||||
|
||||
mv2_params_str = fmt_count(mv2_params.m_as('param'), scale='M', precision=1, commas=False)
|
||||
mv2_params_str = fmt_count(mv2_params.to(param).magnitude, scale='M', precision=1, commas=False)
|
||||
mv2_power_str = fmt_qty(mv2_power, watt, precision=1, commas=False)
|
||||
mv2_latency_str = fmt_qty(mv2_latency, ms, precision=0, commas=False)
|
||||
|
||||
eff_params_str = fmt_count(eff_params.m_as('param'), scale='M', precision=1, commas=False)
|
||||
eff_params_str = fmt_count(eff_params.to(param).magnitude, scale='M', precision=1, commas=False)
|
||||
eff_power_str = fmt_qty(eff_power, watt, precision=1, commas=False)
|
||||
eff_latency_str = fmt_qty(eff_latency, ms, precision=0, commas=False)
|
||||
|
||||
rn50_params_str = fmt_count(rn50_params.m_as('param'), scale='M', precision=1, commas=False)
|
||||
rn50_params_str = fmt_count(rn50_params.to(param).magnitude, scale='M', precision=1, commas=False)
|
||||
rn50_power_str = fmt_qty(rn50_power, watt, precision=1, commas=False)
|
||||
rn50_latency_str = fmt_qty(rn50_latency, ms, precision=0, commas=False)
|
||||
|
||||
tiny_params_str = fmt_count(tiny_params.m_as('param'), scale='K', precision=0, commas=False)
|
||||
tiny_params_str = fmt_count(tiny_params.to(param).magnitude, scale='K', precision=0, commas=False)
|
||||
tiny_power_str = fmt_qty(tiny_power, milliwatt, precision=0, commas=False)
|
||||
tiny_latency_str = fmt_qty(tiny_latency, ms, precision=0, commas=False)
|
||||
tiny_wear_power_margin_str = fmt(tiny_wear_power_margin, precision=0, commas=False)
|
||||
@@ -1675,12 +1675,13 @@ from mlsysim.core.constants import (
|
||||
hour,
|
||||
ms,
|
||||
second,
|
||||
USD,
|
||||
)
|
||||
|
||||
class InferenceCostCalc:
|
||||
"""Training vs. inference TCO comparison for a 10M-user recommendation system."""
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
gpu_rate_value = Infrastructure.Pricing.Cloud.GpuTrainingPerHour.rate.m_as("dollar / hour")
|
||||
gpu_rate_value = Infrastructure.Pricing.Cloud.GpuTrainingPerHour.rate.to(USD / hour).magnitude
|
||||
data_prep_hrs_value = 100 # Data preparation GPU-hours
|
||||
hyperparam_hrs_value = 500 # Hyperparameter search GPU-hours
|
||||
train_hrs_value = 200 # Final training GPU-hours
|
||||
@@ -1689,7 +1690,7 @@ class InferenceCostCalc:
|
||||
users_daily_value = 10_000_000 # Daily active users
|
||||
recs_per_user_value = 20 # Recommendations per user per day
|
||||
inference_ms_value = 10 # Inference latency (ms)
|
||||
gpu_inf_rate_value = Infrastructure.Pricing.Cloud.GpuInferencePerHour.rate.m_as("dollar / hour")
|
||||
gpu_inf_rate_value = Infrastructure.Pricing.Cloud.GpuInferencePerHour.rate.to(USD / hour).magnitude
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
data_prep_cost_value = data_prep_hrs_value * gpu_rate_value
|
||||
@@ -1698,7 +1699,7 @@ class InferenceCostCalc:
|
||||
total_train_cost_value = data_prep_cost_value + hyperparam_cost_value + train_cost_value
|
||||
|
||||
inferences_daily_value = users_daily_value * recs_per_user_value
|
||||
gpu_seconds_daily_value = inferences_daily_value * (inference_ms_value * ms).m_as(second)
|
||||
gpu_seconds_daily_value = inferences_daily_value * (inference_ms_value * ms).to(second).magnitude
|
||||
gpus_needed_value = gpu_seconds_daily_value / (24 * SEC_PER_HOUR)
|
||||
annual_inf_cost_value = gpus_needed_value * 24 * 365 * gpu_inf_rate_value
|
||||
|
||||
@@ -1773,7 +1774,7 @@ Engineers can estimate three-year total cost of ownership using a structured app
|
||||
# │
|
||||
# │ Note: Referenced by tco-calc cell to keep the carbon factor consistent.
|
||||
# └─────────────────────────────────────────────────────────────────────────────
|
||||
from mlsysim.core.constants import watt, ureg, THOUSAND
|
||||
from mlsysim.core.constants import watt, ureg, THOUSAND, kg
|
||||
from mlsysim import Hardware, Infrastructure
|
||||
from mlsysim.fmt import fmt, check, fmt_qty
|
||||
|
||||
@@ -1784,12 +1785,12 @@ class CarbonFactor:
|
||||
carbon_per_gpu_hr = Infrastructure.Pricing.Fleet.CarbonPerGpuHr.rate
|
||||
|
||||
# ┌── 2. EXECUTE (The Compute) ────────────────────────────────────────
|
||||
gpu_power_kw = gpu_power.m_as(watt) / THOUSAND
|
||||
carbon_per_gpu_hr_kg = carbon_per_gpu_hr.m_as(ureg.kilogram)
|
||||
gpu_power_kw = gpu_power.to(watt).magnitude / THOUSAND
|
||||
carbon_per_gpu_hr_kg = carbon_per_gpu_hr.to(kg).magnitude
|
||||
carbon_intensity_kg_kwh = carbon_per_gpu_hr_kg / gpu_power_kw
|
||||
|
||||
# ┌── 3. GUARD (Invariants) ───────────────────────────────────────────
|
||||
check(abs(gpu_power.m_as(watt) - 400) < 1e-9, "A100 baseline power should be 400 W.")
|
||||
check(abs(gpu_power.to(watt).magnitude - 400) < 1e-9, "A100 baseline power should be 400 W.")
|
||||
check(abs(carbon_per_gpu_hr_kg - 0.16) < 1e-9, "GPU-hour carbon factor should be 0.16 kg.")
|
||||
|
||||
# ┌── 4. OUTPUT (Formatting) ─────────────────────────────────────────────
|
||||
@@ -1863,13 +1864,15 @@ from mlsysim.core.constants import (
|
||||
ms,
|
||||
second,
|
||||
ureg,
|
||||
USD,
|
||||
kg,
|
||||
)
|
||||
|
||||
class TCOCalc:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
train_gpu_rate = Infrastructure.Pricing.Cloud.GpuTrainingPerHour.rate.m_as("dollar / hour") # $4/hour
|
||||
inf_gpu_rate = Infrastructure.Pricing.Cloud.GpuInferencePerHour.rate.m_as("dollar / hour")
|
||||
carbon_per_gpu_hr = Infrastructure.Pricing.Fleet.CarbonPerGpuHr.rate.m_as(ureg.kilogram)
|
||||
train_gpu_rate = Infrastructure.Pricing.Cloud.GpuTrainingPerHour.rate.to(USD / hour).magnitude # $4/hour
|
||||
inf_gpu_rate = Infrastructure.Pricing.Cloud.GpuInferencePerHour.rate.to(USD / hour).magnitude
|
||||
carbon_per_gpu_hr = Infrastructure.Pricing.Fleet.CarbonPerGpuHr.rate.to(kg).magnitude
|
||||
t_data_prep_hrs = 100
|
||||
t_hparam_exps = 50
|
||||
t_hparam_cost_exp = 40.0
|
||||
@@ -1878,7 +1881,7 @@ class TCOCalc:
|
||||
i_users = 10_000_000
|
||||
i_recs_per_user = 20
|
||||
i_latency = 10 * ms
|
||||
i_latency_s = i_latency.m_as(second)
|
||||
i_latency_s = i_latency.to(second).magnitude
|
||||
o_monitor_yr = 50000.0
|
||||
o_oncall_yr = 100000.0
|
||||
o_incident_yr = 20000.0
|
||||
@@ -2807,14 +2810,14 @@ Teams treat responsibility as external oversight rather than engineering practic
|
||||
# │ carbon_train_ratio_str, quant_reduction_pct_str,
|
||||
# │ quant_savings_str, quant_carbon_str
|
||||
from mlsysim import Infrastructure
|
||||
from mlsysim.core.constants import DAYS_PER_YEAR, MILLION, SEC_PER_HOUR, THOUSAND, hour, ms, second, ureg
|
||||
from mlsysim.core.constants import DAYS_PER_YEAR, MILLION, SEC_PER_HOUR, THOUSAND, hour, ms, second, ureg, USD, kg
|
||||
from mlsysim.fmt import fmt, fmt_int, fmt_usd, check, fmt_percent
|
||||
|
||||
class ResponsibleTcoRecap:
|
||||
# ┌── 1. LOAD (Constants) ──────────────────────────────────────────────
|
||||
train_gpu_rate = Infrastructure.Pricing.Cloud.GpuTrainingPerHour.rate.m_as("dollar / hour")
|
||||
inf_gpu_rate = Infrastructure.Pricing.Cloud.GpuInferencePerHour.rate.m_as("dollar / hour")
|
||||
carbon_per_gpu_hr = Infrastructure.Pricing.Fleet.CarbonPerGpuHr.rate.m_as(ureg.kilogram)
|
||||
train_gpu_rate = Infrastructure.Pricing.Cloud.GpuTrainingPerHour.rate.to(USD / hour).magnitude
|
||||
inf_gpu_rate = Infrastructure.Pricing.Cloud.GpuInferencePerHour.rate.to(USD / hour).magnitude
|
||||
carbon_per_gpu_hr = Infrastructure.Pricing.Fleet.CarbonPerGpuHr.rate.to(kg).magnitude
|
||||
t_data_prep_hrs = 100
|
||||
t_hparam_exps = 50
|
||||
t_hparam_cost_exp = 40.0
|
||||
@@ -2822,7 +2825,7 @@ class ResponsibleTcoRecap:
|
||||
t_cycles_3yr = 12
|
||||
i_users = 10_000_000
|
||||
i_recs_per_user = 20
|
||||
i_latency_s = (10 * ms).m_as(second)
|
||||
i_latency_s = (10 * ms).to(second).magnitude
|
||||
o_monitor_yr = 50000.0
|
||||
o_oncall_yr = 100000.0
|
||||
o_incident_yr = 20000.0
|
||||
|
||||
Reference in New Issue
Block a user