4040
4141
4242@app .cell
43- def _ ():
43+ async def _ ():
4444 import marimo as mo
4545 import sys
4646 from pathlib import Path
4747 import plotly .graph_objects as go
4848 import numpy as np
4949 import math
5050
51- _root = Path (__file__ ).resolve ().parents [2 ]
52- if str (_root ) not in sys .path :
53- sys .path .insert (0 , str (_root ))
51+ # WASM bootstrap: install mlsysim from hosted wheel when running in browser
52+ if sys .platform == "emscripten" :
53+ import micropip
54+ await micropip .install ("https://mlsysbook.ai/labs/wheels/mlsysim-0.1.0-py3-none-any.whl" )
55+ elif "mlsysim" not in sys .modules :
56+ _root = Path (__file__ ).resolve ().parents [2 ]
57+ if str (_root ) not in sys .path :
58+ sys .path .insert (0 , str (_root ))
5459
55- from labs .core .state import DesignLedger
56- from labs .core .style import COLORS , LAB_CSS , apply_plotly_theme
60+ from mlsysim .labs .state import DesignLedger
61+ from mlsysim .labs .style import COLORS , LAB_CSS , apply_plotly_theme
62+ import mlsysim
5763
58- # ── Hardware constants (NVIDIA published specs + chapter equations ) ──
64+ # ── Hardware constants (extracted from mlsysim for reference ) ──
5965 # H100 SXM5: https://www.nvidia.com/en-us/data-center/h100/
60- H100_BW_GBS = 3350 # GB/s HBM3e memory bandwidth
61- H100_TFLOPS_FP16 = 989 # TFLOPS FP16 dense tensor core — NVIDIA H100 SXM5 spec
62- H100_RAM_GB = 80 # GB HBM capacity
66+ H100_BW_GBS = mlsysim . Hardware . Cloud . H100 . memory . bandwidth . m_as ( " GB/s" )
67+ H100_TFLOPS_FP16 = mlsysim . Hardware . Cloud . H100 . compute . peak_flops . m_as ( "TFLOPs/s" )
68+ H100_RAM_GB = mlsysim . Hardware . Cloud . H100 . memory . capacity . m_as ( "GB" )
6369
6470 # A100 SXM4: prior-generation reference for CTO upgrade scenario
65- A100_BW_GBS = 2000 # GB/s HBM2e (actual: 1935, rounded up for scenario )
66- A100_TFLOPS_FP16 = 312 # TFLOPS FP16 Tensor Core peak
71+ A100_BW_GBS = mlsysim . Hardware . Cloud . A100 . memory . bandwidth . m_as ( " GB/s" )
72+ A100_TFLOPS_FP16 = mlsysim . Hardware . Cloud . A100 . compute . peak_flops . m_as ( "TFLOPs/s" )
6773
6874 # Jetson Orin NX: NVIDIA embedded inference platform
69- ORIN_BW_GBS = 102 # GB/s LPDDR5 memory bandwidth
70- ORIN_TOPS = 100 # TOPS INT8 equivalent
71- ORIN_RAM_GB = 16 # GB
75+ ORIN_BW_GBS = mlsysim . Hardware . Edge . Jetson . memory . bandwidth . m_as ( " GB/s" )
76+ ORIN_TOPS = mlsysim . Hardware . Edge . Jetson . compute . precision_flops . get ( "int8" , mlsysim . Hardware . Edge . Jetson . compute . peak_flops ). m_as ( "TFLOPs/s" )
77+ ORIN_RAM_GB = mlsysim . Hardware . Edge . Jetson . memory . capacity . m_as ( "GB" )
7278
7379 # Speed of light in fiber — from @eq-latency-physics (ml_systems.qmd):
7480 # Latency_min = 2 * Distance / (0.67 * c) ≈ 2 * Distance / 200,000 km/s
@@ -83,7 +89,7 @@ def _():
8389
8490 return (
8591 mo , ledger , COLORS , LAB_CSS , apply_plotly_theme ,
86- go , np , math ,
92+ go , np , math , mlsysim ,
8793 H100_BW_GBS , H100_TFLOPS_FP16 , H100_RAM_GB ,
8894 A100_BW_GBS , A100_TFLOPS_FP16 ,
8995 ORIN_BW_GBS , ORIN_TOPS , ORIN_RAM_GB ,
@@ -449,44 +455,58 @@ def _(
449455 _hw_new = "Jetson Orin NX"
450456 _hw_old = "Prior Orin (20% compute)"
451457
452- # ── Iron Law calculation ──────────────────────────────────────────────────
453- # Model parameters for a representative transformer layer inference:
454- # D = 2 GB (weights loaded per inference for a ~1B param slice in FP16)
455- # O = AI * D (by definition of Arithmetic Intensity)
456- # Source: @sec-ml-systems-architectural-anchor (Memory Wall)
458+ # ── Flight Simulator: MLSys·im Engine Evaluation ──────────────────────────
459+ import mlsysim
460+
457461 _AI = act1_ai_slider .value # FLOPs/Byte — user control
458462 _D_GB = 2.0 # GB data moved per inference call
459463 _D_bytes = _D_GB * 1e9 # bytes
460-
461- # Operations = AI × data
462464 _O_flops = _AI * _D_bytes # FLOPs
463465
464- # ── New hardware (H100 or Orin) ──────────────────────────────────────────
465- _bw_new_bps = _bw_new * 1e9 # bytes/s
466- _r_new_fps = _r_new * 1e12 # FLOPs/s
466+ # Construct the workload for the Engine
467+ mock_workload = mlsysim .Models .CNNWorkload (
468+ name = f"Layer_Slice_AI_{ _AI } " ,
469+ architecture = "Generic" ,
470+ parameters = mlsysim .Q_ (_D_bytes / 2 , "count" ), # Assume FP16 (2 bytes/param)
471+ inference_flops = mlsysim .Q_ (_O_flops , "flop" )
472+ )
467473
468- _t_mem_new_ms = (_D_bytes / _bw_new_bps ) * 1000 # ms
469- _t_comp_new_ms = (_O_flops / _r_new_fps ) * 1000 # ms
470- _t_ovh_new_ms = OVERHEAD_MS # ms (fixed)
471- _t_total_new = _t_mem_new_ms + _t_comp_new_ms + _t_ovh_new_ms
474+ # Resolve hardware objects
475+ _hw_obj_new = mlsysim .Hardware .Cloud .H100 if _ctx == "cloud" else mlsysim .Hardware .Edge .Jetson
476+ _hw_obj_old = mlsysim .Hardware .Cloud .A100 if _ctx == "cloud" else mlsysim .Hardware .Tiny .ESP32 # Using ESP32 as a tiny baseline
477+
478+ # Override dispatcher tax to match the fixed OVERHEAD_MS for the lab
479+ _hw_obj_new .dispatch_tax = mlsysim .Q_ (OVERHEAD_MS , "ms" )
480+ _hw_obj_old .dispatch_tax = mlsysim .Q_ (OVERHEAD_MS , "ms" )
481+
482+ # Evaluate New Hardware
483+ profile_new = mlsysim .Engine .solve (
484+ model = mock_workload , hardware = _hw_obj_new , batch_size = 1 , precision = "fp16" , efficiency = 1.0
485+ )
486+ # Evaluate Old Hardware
487+ profile_old = mlsysim .Engine .solve (
488+ model = mock_workload , hardware = _hw_obj_old , batch_size = 1 , precision = "fp16" , efficiency = 1.0
489+ )
472490
473- # ── Old hardware (A100 or prior Orin) ───────────────────────────────────
474- _bw_old_bps = _bw_old * 1e9
475- _r_old_fps = _r_old * 1e12
491+ # Extract component times
492+ _t_mem_new_ms = profile_new .latency_memory .m_as ("ms" )
493+ _t_comp_new_ms = profile_new .latency_compute .m_as ("ms" )
494+ _t_ovh_new_ms = profile_new .latency_overhead .m_as ("ms" )
495+ # To match the classic strict addition in the textbook's first introduction:
496+ _t_total_new = _t_mem_new_ms + _t_comp_new_ms + _t_ovh_new_ms
476497
477- _t_mem_old_ms = ( _D_bytes / _bw_old_bps ) * 1000
478- _t_comp_old_ms = ( _O_flops / _r_old_fps ) * 1000
479- _t_ovh_old_ms = OVERHEAD_MS
498+ _t_mem_old_ms = profile_old . latency_memory . m_as ( "ms" )
499+ _t_comp_old_ms = profile_old . latency_compute . m_as ( "ms" )
500+ _t_ovh_old_ms = profile_old . latency_overhead . m_as ( "ms" )
480501 _t_total_old = _t_mem_old_ms + _t_comp_old_ms + _t_ovh_old_ms
481502
482503 # ── Ridge points ─────────────────────────────────────────────────────────
483- # Ridge point = R / BW (FLOPs/Byte at which compute term = memory term)
484- _ridge_new = (_r_new * 1e12 ) / (_bw_new * 1e9 ) # FLOPs/Byte
485- _ridge_old = (_r_old * 1e12 ) / (_bw_old * 1e9 )
504+ _ridge_new = _hw_obj_new .ridge_point ().m_as ("flop/byte" )
505+ _ridge_old = _hw_obj_old .ridge_point ().m_as ("flop/byte" )
486506
487507 # ── Bottleneck classification ─────────────────────────────────────────────
488- _is_mem_bound_new = _AI < _ridge_new
489- _bottleneck_new = "Memory-bound" if _is_mem_bound_new else "Compute -bound"
508+ _is_mem_bound_new = profile_new . bottleneck == "Memory"
509+ _bottleneck_new = profile_new . bottleneck + " -bound"
490510 _bottleneck_color = COLORS ["RedLine" ] if _is_mem_bound_new else COLORS ["BlueLine" ]
491511
492512 # ── Latency improvement ───────────────────────────────────────────────────
@@ -545,20 +565,36 @@ def _(
545565 apply_plotly_theme (_fig )
546566
547567 # ── Physics formula display ───────────────────────────────────────────────
548- _formula_block = f"""
549- **Iron Law — Live Calculation** (`AI = { _AI } FLOPs/Byte, D = { _D_GB :.1f} GB`)
550-
551- ```
552- Memory D/BW = { _D_GB :.1f} GB / { _bw_new :,} GB/s = { _t_mem_new_ms :.3f} ms ← { _hw_new }
553- Compute O/R = { _AI * _D_GB :.1f} GFLOPS / { _r_new :,} TFLOPS = { _t_comp_new_ms :.4f} ms
554- Overhead L = { _t_ovh_new_ms :.1f} ms (fixed dispatch tax)
555- ─────────────────────────────────────────────────────────────
556- Total T = { _t_total_new :.3f} ms (vs { _t_total_old :.3f} ms on { _hw_old } )
557-
558- Ridge Point = R / BW = { _ridge_new :,.0f} FLOPs/Byte ({ _hw_new } )
559- AI = { _AI } FLOPs/Byte → { 'BELOW ridge → Memory-bound' if _is_mem_bound_new else 'ABOVE ridge → Compute-bound' }
560- ```
561- """
568+ _accordion = mo .accordion ({
569+ "⚙️ Under the Hood: How MLSys·im Calculates This" : mo .md (f"""
570+ This "Flight Simulator" uses the exact same `mlsysim` physics engine as the textbook.
571+ Here is the code running in the background to calculate the Iron Law terms:
572+
573+ ```python
574+ import mlsysim
575+
576+ # 1. Define the hardware and workload
577+ hw = mlsysim.Hardware.Cloud.H100 if _ctx == 'cloud' else mlsysim.Hardware.Edge.Jetson
578+ workload = mlsysim.Models.Generic(
579+ parameters=mlsysim.Q_({ _D_bytes / 2 } , "count"), # Assume FP16
580+ inference_flops=mlsysim.Q_({ _O_flops } , "flop")
581+ )
582+
583+ # 2. Evaluate the theoretical performance
584+ profile = mlsysim.Engine.solve(
585+ model=workload,
586+ hardware=hw,
587+ batch_size=1,
588+ precision="fp16",
589+ efficiency=1.0
590+ )
591+
592+ print(f"Memory Term: {{profile.latency_memory}}")
593+ print(f"Compute Term: {{profile.latency_compute}}")
594+ print(f"Overhead Term: {{profile.latency_overhead}}")
595+ ```
596+ """ )
597+ })
562598
563599 # ── Metric cards ──────────────────────────────────────────────────────────
564600 _mem_pct = _t_mem_new_ms / _t_total_new * 100 if _t_total_new > 0 else 0
@@ -635,7 +671,7 @@ def _(
635671 ),
636672 mo .as_html (_fig ),
637673 mo .Html (_cards_html ),
638- mo . md ( _formula_block ) ,
674+ _accordion ,
639675 ])
640676 # Export bottleneck for downstream cells
641677 return (
@@ -1467,6 +1503,17 @@ def _(mo, COLORS):
14671503 text-transform: uppercase; letter-spacing: 0.12em; margin-bottom: 12px;">
14681504 Key Takeaways
14691505 </div>
1506+
1507+ <div style="background:linear-gradient(to right, #f8fafc, #f1f5f9); border-radius:8px; padding:20px; margin-bottom:24px; border-left:4px solid #8b5cf6;">
1508+ <div style="font-weight:800; font-size:1.1rem; color:#6d28d9; margin-bottom:8px;">💎 The Iron Law Nugget</div>
1509+ <div style="color:#334155; font-size:1rem; font-style:italic; line-height:1.6;">
1510+ "The Iron Law of ML Systems states that performance is bounded by the slowest of three things: Computation, Communication, or Memory. You cannot optimize a system without knowing which one is binding."
1511+ </div>
1512+ <div style="margin-top:12px; font-size:0.8rem; color:#64748b;">
1513+ <strong>Source:</strong> Formalized in <em>Reddi, V. J., et al. (2025). Machine Learning Systems. Chapter 2: ML Systems.</em> (Adapted from the fundamental limits defined in Patterson & Hennessy).
1514+ </div>
1515+ </div>
1516+
14701517 <div style="font-size: 0.92rem; color: { COLORS ['Text' ]} ; line-height: 1.75;">
14711518 <div style="margin-bottom: 10px;">
14721519 <strong>1. The Memory Wall dominates transformer inference at low Arithmetic Intensity.</strong>
0 commit comments