Visual walkthrough — Heterogeneous computing concepts
Step 1 — Draw the whole job as a bar of time
WHAT. Imagine the entire program running on the CPU alone. We do not care yet what it does — we only care how long it takes. Draw that total time as one long bar.
WHY. Before we can talk about "speeding things up", we need one honest baseline: how long the slow, ordinary way takes. Everything else will be measured against this bar, so we give it the friendly value — one "whole job's worth of time".
PICTURE. The bar below is the whole job; its length is exactly . The horizontal axis is time as a fraction of the whole job (dimensionless, to ); there is no vertical scale — the bar's height is only for visibility.

Step 2 — Split the bar into what CAN and CAN'T go to the GPU
WHAT. Cut the bar into two coloured pieces. One piece is work that must stay on the CPU (reading files, awkward if/else logic, glue code). The other piece is work that is the same operation repeated over millions of numbers — the piece a GPU loves.
WHY. A GPU cannot speed up everything. Only the repetitive, parallel part benefits. So the very first move in any real analysis is to ask "how much of my job is even eligible for the accelerator?" That question splits the bar.
Every term here is a length along the bar. If then of the bar is coloured "GPU-eligible" and only stays "CPU-only".
PICTURE. Same bar and same horizontal axis as Step 1 (time as a fraction of the whole job, to ), now split into two colours whose lengths add back to the full . The tick at marks where serial ends and parallel begins.

Step 3 — Shrink only the parallel slice
WHAT. Send the parallel slice to the GPU. The GPU chews through it faster, so that coloured slice gets shorter. The serial slice does not move — it keeps its full length.
WHY. This is the heart of the whole idea. Speedup is not magic that shortens the whole bar; it only shrinks the part that ran on the faster machine. We need one number that says "how much faster is the GPU on this slice?"
So the parallel slice, originally length , becomes:
PICTURE. Two bars stacked, sharing the same time axis (fraction of the whole job, to ): the "before" bar on top, the "after" bar below. Watch the pink slice collapse while the yellow serial slice stubbornly stays the same length.

Step 4 — Add the tax nobody talks about: moving the data
WHAT. The GPU has its own memory (VRAM). To use it, the CPU must copy the data in, and later copy the result back. That copying takes time — and it happens no matter how fast the GPU computes.
WHY. In Step 3 we quietly assumed the GPU slice was "free to hand over". It is not. This is the piece that turns a beautiful theory into engineering reality — and it is the piece that will build the wall. We name this cost.
First, name the two ingredients of any copy. Copying takes longer when there are more bytes to copy, and shorter when the pipe is fatter. So we need exactly two named quantities:
How much data is there? It depends on how big the parallel slice is. A bigger parallel slice ( closer to ) usually means more numbers to ship, so is really a function of . We write that dependence explicitly as — read it " as a function of ", i.e. "the number of bytes we must move when the parallel fraction is ". The key facts we need are only these two: grows as grows, and (no parallel slice ⇒ no bytes to move).
Now stack the three pieces to get the new total time:
The subscript hetero means "on the heterogeneous CPU+GPU system". Read the equation left to right and you are literally reading the new bar left to right.
PICTURE. The new bar on the same time axis (fraction of the whole job): full yellow serial slice + tiny pink shrunk slice + a grey tax block whose length is , i.e. it depends on how much parallel data we shipped.

Step 5 — Compare old bar to new bar → the speedup formula
WHAT. Speedup is just "how many times shorter did the bar get?" Old bar over new bar.
WHY. We want a single number to report to a boss: "we made it times faster." That number is a ratio of the two bar lengths we just drew.
Plug in the parent note's numbers (, , , and for this workload ):
Fifty-times-faster hardware, eleven-times-faster program. The two little constants and ate most of it.
PICTURE. Old bar on top, new bar below, both on the same time axis (fraction of the old job, to ), with a bracket showing "≈ 11× shorter, not 50×".

Step 6 — The wall: push the GPU to infinity
WHAT. Now do a thought experiment. Suppose the GPU became infinitely fast — the shrunk slice vanishes entirely, .
WHY. This is the classic move to find the ceiling. If even a perfect GPU can't remove the serial slice and the tax, then those two together are a hard limit no hardware can beat. We use a limit because it answers the exact question: "what is the best possible outcome?"
So even a magical GPU caps out around for this workload. The we already got is startlingly close to the ceiling — buying faster hardware would barely help. The serial slice and the transfer tax are the wall.
PICTURE. Horizontal axis: (how many times faster the GPU is, dimensionless). Vertical axis: (overall program speedup, dimensionless). The blue curve climbs as grows, then flattens against a dashed pink ceiling line at .

Step 7 — Edge & degenerate cases (never leave a gap)
WHAT. Every case a reader could hit. We test the formula at its extremes so nothing surprises you.
WHY. A formula you trust only in the "nice" middle is a trap. Push it to , to , to no-tax, and check it says something sensible. Crucially, we now respect that depends on : no parallel work ⇒ no data moved ⇒ no tax.
PICTURE. Five mini-bars stacked, all on the same time axis (fraction of the old job): a dotted vertical line marks the old baseline length , and each case's new bar is drawn to true length so you can see it land at, below, or past the baseline.

The one-picture summary
Everything above, compressed: start with a bar of length , split it into serial (yellow, immovable) and parallel (pink, shrinkable) with , collapse the pink by , glue on the grey transfer tax (which is only there because there is pink to move), then read the speedup as old length ÷ new length — bounded forever by the yellow+grey stub.

Recall Feynman retelling — say it back in plain words
Picture your whole program as one chocolate bar, and its length is how long it takes.
Some squares of the bar are "boring glue work" (reading files, weird logic) — those have to stay on the CPU, and they never get shorter. The rest of the bar is "the same math a million times" — those squares can go to the GPU. The two kinds of squares always add up to the whole bar, and you can't have negative squares or more than a full bar.
The GPU is fast, so it shrinks only those math squares, dividing their length by how many times faster it is. But there's a catch: you have to carry the data over to the GPU and carry the answer back, and that carrying costs a grey chunk. How big is that grey chunk? It's the number of bytes you move () divided by how fast the wire is (), times two for the round trip. And the number of bytes only exists because there are math squares to feed — if none of the bar can go to the GPU, there's nothing to carry and no grey chunk at all.
So the new bar = the immovable glue squares + the tiny shrunken math squares + the grey carrying chunk. Your speedup is just how many times shorter the new bar is than the old one.
The punchline: as long as there is real GPU work, even an infinitely fast GPU still leaves the glue squares and the grey carrying chunk. They set a wall you can never break through. That's why a 50×-faster GPU only gave an 11× program — we were already almost touching the wall.
See also: Parallel Programming Models · Memory Hierarchy and Caching · Power and Energy Optimization · SIMD and Vector Processing · Amdahl's Law and Scalability