4.5.10 · D3Software Engineering

Worked examples — CI - CD — pipeline stages, GitHub Actions - GitLab CI concepts

3,551 words16 min readBack to topic

Figure — CI - CD — pipeline stages, GitHub Actions - GitLab CI concepts
Figure s01 — Left: 8 jobs = 2 full waves. Right: 9 jobs = 3 waves, the last one mostly idle. The ceiling is the maths word for "one leftover forces a whole extra wave."


The scenario matrix

Every timing question this topic can throw is one of these cells. Each worked example below is tagged with the cell it covers.

Cell Case class What is special Example
A (runners ≥ jobs) Everything runs in one wave; extra runners wasted Ex 1
B , divides evenly Clean waves, no idle runners Ex 2
C , does not divide Last wave has idle runners; ceiling bites Ex 3
D (degenerate) Parallel collapses to serial Ex 4
E (empty stage) Zero jobs — limiting/degenerate input Ex 4
F Caching swap Time saved per run, over many runs Ex 5
G Dependent stages (serial + parallel mixed) Add stage times, parallel within each Ex 6
H Real-world word problem Translate English → Ex 7
I Limiting behaviour / speedup cap "More runners" past Ex 8
J Exam twist (unequal job times) The equal- assumption breaks Ex 9

Example 1 — Cell A: more runners than jobs

  1. Identify the cell. Why this step? Choosing the right formula depends on whether . Here , so we are in Cell A — one wave suffices.
  2. Count the waves. Why this step? Waves . One wave.
  3. Compute time. Why this step? Each wave costs , so min.
  4. Count busy runners. Why this step? Only jobs exist, so runners sit idle — you paid for machines you didn't use.

Example 2 — Cell B: fewer runners, divides evenly

  1. Cell check. Why? , and is a whole number → Cell B, no leftover.
  2. Waves. Why? . Both waves are full (4 jobs each), no idle runners.
  3. Time. Why? min.
  4. Speedup. Why? , so .

Example 3 — Cell C: ceiling bites (idle runners in last wave)

  1. Cell check. Why? is not whole → Cell C, the leftover job forces an extra wave.
  2. Waves. Why? . Waves 1 and 2 are full (4+4=8 jobs), wave 3 holds the last 1 job.
  3. Time. Why? min.
  4. Idle slots. Why? In wave 3, runners do nothing. So , which is less than — the ceiling penalty.

Figure s02 draws all three waves as rows of lavender boxes. Rows 1 and 2 are full (four jobs each); row 3 shows one lavender job beside three grey "idle" boxes. Read the row labels down the left: each wave adds 2 minutes, so the stack totals min, printed in coral at the bottom.

Figure — CI - CD — pipeline stages, GitHub Actions - GitLab CI concepts
Figure s02 — 9 jobs on 4 runners: the third wave carries a single job while 3 runners idle, dragging speedup down to 3 (below m=4).


Example 4 — Cells D & E: the degenerate inputs

  1. Case D, cell check. Why? means only one job runs at a time — parallelism is switched off.
  2. Case D, waves. Why? waves. min . Parallel formula collapses into the serial formula — good, they must agree at .
  3. Case E, cell check. Why? : there is nothing to run. waves.
  4. Case E, time. Why? min. An empty stage is instant (in this idealised model — real engines have tiny overhead, but the formula says 0).
Recall Why we always test the degenerate inputs

A formula you trust must survive its boundaries. must reduce to serial; must give 0. If a formula misbehaves at the edges, it is wrong in the middle too. This is the same discipline as checking at the quadrant boundaries.


Example 5 — Cell F: caching over a day

  1. Per-run time before. Why? Without cache each run pays s.
  2. Per-run time after. Why? A cache hit swaps for : s. Note is untouched — caching only attacks the install part.
  3. Saving per run. Why? s. This is the whole payoff of caching, per run.
  4. Daily saving in seconds. Why? Multiply the per-run saving by the number of runs: s.
  5. Convert seconds to minutes. Why this step? We were asked for the answer in minutes, but every input above was in seconds, so the raw total is in seconds. To change units we divide by (there are 60 seconds in a minute): min/day. Skipping this step would report "3360" as if it were minutes — a 60× error.

Example 6 — Cell G: mixed serial stages with parallel jobs

  1. Build time. Why? One job → wave → min.
  2. Test time. Why? waves → min. (Test is not just 3 min — the 6 shards need 2 waves.)
  3. Deploy time. Why? One job → min.
  4. Sum the stages. Why? Sequential stages add: min.

Figure s03 lays the pipeline out left-to-right: a mint Build box, an arrow into a 2×3 grid of butter Test boxes (the two waves of three), an arrow into a coral Deploy box. The arrows are the DAG dependencies that force order; the caption sums the stage times to 12 minutes.

Figure — CI - CD — pipeline stages, GitHub Actions - GitLab CI concepts
Figure s03 — Stages chained by arrows add their times (4 + 6 + 2 = 12 min); the Test stage itself is two parallel waves of three jobs.


Example 7 — Cell H: real-world word problem

  1. Translate to symbols. Why? Word problems fail when you skip this. "240 tests, 5 s each, one after another" → , s, run serially. "10 equal shards on 10 runners" → generic shards, runners, each shard running tests.
  2. Before (laptop, serial). Why? All 240 tests one after another: s min (÷60 to convert seconds to minutes, since the ask is in minutes).
  3. Per-shard time. Why? Each shard runs 24 tests: generic s.
  4. After (10 shards, 10 runners). Why? wave, all shards at once → s min.

Example 8 — Cell I: the limiting behaviour (speedup cap)

  1. Build the table. Why? We want to see the cap, not just assert it.
waves waves
1 8 24 1
2 4 12 2
4 2 6 4
8 1 3 8
16 1 3 8
  1. Read the cap. Why? From to , stays at min and stays at . Speedup flattens the moment .
  2. Explain it. Why? You cannot split 8 independent jobs across more than 8 machines at once — the 9th machine has nothing to do. So always.

Figure s04 plots (lavender line, coral dots) against . The curve climbs then goes flat as a table-top at ; a mint dashed line marks the ceiling , and a coral arrow labels the flat part "extra runners useless."

Figure — CI - CD — pipeline stages, GitHub Actions - GitLab CI concepts
Figure s04 — Speedup rises with runners but flattens hard at S = n = 8: past m = 8 the extra machines add nothing.


Example 9 — Cell J: exam twist, unequal job times

  1. Simulate the greedy schedule. Why? With unequal times the answer depends on placement. Sort jobs descending: .
    • : Runner A takes 7, Runner B takes 3.
    • : B is free, takes the next 3 (finishes at 6).
    • : B free, takes 1 (finishes at 7).
    • A finishes its 7 at .
  2. Read the finish. Why? The pipeline is done when the last runner finishes: min.
  3. Naive formula for contrast. Why? Using average min and waves gives min — here it happens to match, but only by luck of these numbers.
  4. Break the coincidence. Why this step? One matching case does not prove the naive formula safe — we must show a case where it fails. Change the jobs to (same , same ). Greedy: A takes the 9 (finishes at 9); B mops up (finishes at 3). True finish min. But the naive formula uses average min and waves, giving min. So naive says 6 while reality is 9 — the naive formula under-estimates by 3 minutes. The single "elephant" job (the 9) sets the floor; averaging hides it.

Wrap-up

Recall Which cell did each example cover?

Ex1 :::> A () Ex2 :::> B (divides evenly) Ex3 :::> C (ceiling bites) Ex4 :::> D & E (, ) Ex5 :::> F (caching) Ex6 :::> G (mixed serial + parallel) Ex7 :::> H (word problem) Ex8 :::> I (speedup cap) Ex9 :::> J (unequal job times)

See also: the parent topic, and for the theory of the cap, Amdahl's Law. The sharding idea sits on top of Automated Testing — Unit vs Integration vs E2E, runs on Docker & Containerization runners, and expresses the DevOps Principles & Shift-Left philosophy.