Pipeline Bubble Viz

An interactive visualisation of the pipeline-parallelism bubble — the idle GPU time that pipelined training schedules can't avoid. The classic GPipe formula is simple:

bubble fraction = (P - 1) / (M + P - 1)

— but the consequences (peak activation memory, the warmup / cooldown shape, the 1F1B memory-vs-bubble trade-off) are not obvious until you watch the schedule fill in cell by cell. GPipe runs all forwards top-to-bottom, then all backwards bottom-to-top (peak activation memory: M). 1F1B warms up P-1 forwards, then alternates one forward and one backward per stage (peak activation memory: P).

Bubble fraction42.9%(3) / (4 + 3) = 3/7
24 idle / 56 total GPU-slots
IdleActive
012345678910111213GPU 0GPU 1GPU 2GPU 30000111122223333BBBBBBBBBBBBBBBB
F0
F1
F2
F3
Backward
Bubble (idle)
Schedule
Stages (P)
Micro-batches (M)
Space: play/pause · Left/Right: step · 1: GPipe · 2: 1F1B
GPipe schedule: 4 stages, 4 micro-batches. Bubble: 42.9%.

GPipe schedule: 4 GPU stages, 4 micro-batches. Forwards cascade down, then all backwards cascade up. The hatched cells are the bubble — 42.9% of GPU time wasted.

Customize
Schedule
gpipe
Pipeline shape
4
4

Installation

npx shadcn@latest add https://craftbits.dev/r/pipeline-bubble-viz.json

Usage

import { PipelineBubbleViz } from "@craft-bits/viz/pipeline-bubble-viz";
 
<PipelineBubbleViz />

Start the learner on 1F1B with 8 stages and 16 micro-batches:

<PipelineBubbleViz
  defaultSchedule="1f1b"
  defaultStages={8}
  defaultMicroBatches={16}
/>

React when the schedule selection changes:

<PipelineBubbleViz
  onScheduleChange={(s) => console.log("now showing", s)}
/>

Understanding the component

  1. Grid layout. The SVG renders a P × T grid where rows are GPU stages and columns are time steps. Forward cells take the micro-batch's colour; backward cells use the same hue at 60% opacity and add a B label; bubble cells are hatched at low opacity.
  2. Two schedule builders. buildGPipeSchedule lays forwards along the diagonal t = m + s, places backwards in reverse after all forwards finish, and fills the remainder with bubble cells. build1F1BSchedule runs a small constraint solver — for each micro-batch it advances stage by stage, respecting per-stage nextFree, forward dependency on the previous stage, and backward dependency on the next stage — so warmup, steady-state, and cooldown emerge naturally.
  3. Animated playback. The Animate button reveals work cells in chronological order, dimming unrevealed cells to 8% opacity. The active cell pops to 1.08× scale and gains a focus glow filter. Speed adapts to M — denser schedules tick faster so the playthrough stays short.
  4. Live bubble fraction. The big tabular-nums readout re-animates on every P / M / schedule change. A proportional bar splits idle vs active GPU-slots so the wasted area is visible at a glance, even before the learner has read the percentage.
  5. Comparison callout. Once the learner has toggled between schedules, a per-schedule callout appears explaining the peak-memory trade-off and the bubble's structural shape. The narration band tints info while animating and success after comparison.

Props

PropTypeDefaultDescription
stageOptionsreadonly number[][2, 4, 8]Stage-count (P) options surfaced as buttons.
microBatchOptionsreadonly number[][4, 8, 16, 32]Micro-batch (M) options surfaced as buttons.
defaultStagesnumber4Initial stage count.
defaultMicroBatchesnumber4Initial micro-batch count.
defaultSchedulePipelineBubbleVizSchedule"gpipe"Initial schedule.
transitionTransitionSPRINGS.snapOverride the spring for readout pops and AnimatePresence transitions.
onScheduleChange(schedule) => voidFires when the schedule selection changes.
classNamestringMerged onto the root via cn().

Accessibility

  • The root is a role="figure" with an aria-label that summarises the current schedule, P, M, and bubble fraction, so screen-reader users get a one-line description before exploring the grid.
  • A polite live region carries the animation step (e.g. "Animating GPipe schedule, step 14 of 40") and the static schedule summary when idle.
  • Every interactive control is a real <button> with aria-pressed for toggled state and a descriptive aria-label.
  • Keyboard support: Space plays/pauses, ArrowLeft / ArrowRight step the animation, and 1 / 2 switch between GPipe and 1F1B.
  • Colour is never the only signal: backward cells carry a B label, forward cells show their micro-batch index when the cell is large enough, and bubble cells are hatched in addition to being muted.
  • Motion respects prefers-reduced-motion: reduce — the readout pop, the bar width animation, the reveal-on-step glow, and the comparison callout entrance all collapse to instant transitions.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/systems/PipelineBubbleViz.tsx). The source was a lesson primitive that consumed project-specific colour vars (--color-ink-*, --color-accent-400, --color-success-400, --color-warn-400) and inline springs (SPRINGS.snappy, SPRINGS.gentle). The viz extract rekeys every spring to the canonical SPRINGS.snap / SPRINGS.smooth from @craft-bits/core/motion, remaps colours to var(--cb-accent) / var(--cb-success) / var(--cb-warning) / var(--cb-info) / var(--cb-fg-*) / var(--cb-bg-*) so consumer themes repaint freely, exposes the previously hard-coded option arrays as stageOptions / microBatchOptions props alongside defaultStages, defaultMicroBatches, and defaultSchedule for full control, and lifts onScheduleChange as a callback so the host page can react to mode flips.