Four Kinds of Parallelism
Instruction-level, data-level, thread-level and core-level parallelism are four different mechanisms with four different requirements, four different costs and four different failure modes. Most confused performance arguments come from conflating two of them.
Software view, hardware view
The gap between what you wrote and what the machine does is where this whole domain lives.
The four mechanisms side by side
They differ along every axis that matters: who creates the parallelism, what the work has to look like to qualify, what it costs to use, and what goes wrong when it does not fit. Reading them as one thing called "parallelism" makes every one of those distinctions invisible.
Note especially the ordering by risk. Instruction-level parallelism is extracted by hardware and cannot produce a wrong answer. Data-level parallelism is generated by the compiler or by explicit vector code and is still single-threaded, so it also cannot race. Thread-level and core-level parallelism introduce shared mutable state and everything that comes with it — synchronisation, coherence traffic, and a class of bugs the first two cannot produce.
They also compose multiplicatively rather than exclusively. A well-optimised numeric kernel typically uses all four: independent chains within an iteration, vector instructions across elements, and several threads on several cores. Each layer multiplies the one below, which is why fixing the bottom layers first pays off at every layer above.
| Instruction-level | Data-level (SIMD) | Thread-level (SMT) | Core-level | |
|---|---|---|---|---|
| Created by | Hardware, automatically | Compiler or explicit vector code | OS threads on one core | OS threads on several cores |
| Requires | Independent operations nearby | Same operation over many elements | Work that stalls often | Coarse independent work |
| Correctness risk | None | None — still one thread | Races on shared state | Races, plus coherence traffic |
| Typical limiter | Dependency chains | Layout, aliasing, branches | Shared execution resources | Synchronisation, memory bandwidth |
| Programmer control | Indirect — structure the code | Layout and hints | Little beyond enabling it | Full, and full responsibility |
| Where to read | Instruction-Level Parallelism | SIMD: One Instruction, Many Elements | SMT: Two Contexts, One Core | What a Second Core Actually Adds |
Choosing which one your problem admits
The requirements are genuinely different, and a problem can qualify for one and not another. A loop summing an array qualifies for all four. A loop walking a linked list qualifies for essentially none — the dependency chain kills instruction-level parallelism, the irregular addresses kill vectorisation, and splitting a traversal across threads requires knowing where the list is, which you do not until you have walked it.
That asymmetry is the practical point. Before asking how to parallelise, ask what shape the work has. If the data is contiguous and the operation uniform, the cheap mechanisms apply and should be used first. If the work is a serial chain through memory, no amount of threading helps and the real fix is a different data structure (Both Are O(n). One Is Far Slower., Data-Oriented Design, Without the Dogma).
The ordering that follows is: fix the dependency structure, then vectorise, then thread. Each step makes the next more effective, and each is cheaper and safer than the one after it. Reversing the order is how teams end up with a threaded implementation of a memory-bound loop that scales to two cores and then stops.
Where they stop composing
The multiplication is not unlimited, and the ceiling is usually memory. Vectorising increases the rate at which a core consumes data; threading increases the number of cores consuming it. Both push against finite memory bandwidth, and a kernel that was compute-bound in scalar single-threaded form is frequently bandwidth-bound after both transformations (When the Memory Bus Is the Bottleneck).
This is why the four mechanisms should be applied with measurement between them rather than all at once. The transformation that would have given a large speedup on a compute-bound loop gives very little once the loop has become bandwidth-bound, and the counters will say so — the same reasoning Busy Is Not the Same as Working formalises.
The second ceiling is Amdahl-shaped and applies only to the threaded mechanisms: whatever fraction of the work is serial bounds the total speedup regardless of core count. Instruction- and data-level parallelism do not have this property in the same form, because they operate within the serial portion rather than around it — another reason to exhaust them first.
Key points
- Four mechanisms — instruction, data, thread and core level — with different requirements, costs and failure modes.
- The first two carry no correctness risk; the last two introduce shared mutable state and everything that follows from it.
- They compose multiplicatively, so fixing the cheapest layers first improves everything above them.
- A problem can qualify for some and not others; a pointer chase qualifies for essentially none.
- The composition ceiling is usually memory bandwidth, not core count.
Follow the mechanism
The path through the machine, hop by hop — and the conclusions it invites that are wrong.
- 1Source → hardware window: independent operations within one stream overlap automatically, with no software involvement.
- 2Compiler → vector units: uniform operations over contiguous elements become single instructions covering several elements.
- 3OS → hardware contexts: several threads share one core's execution resources, filling slots left idle by stalls.
- 4OS → cores: independent threads run genuinely simultaneously on separate cores with separate caches.
- 5All four → memory system: each layer increases demand on shared bandwidth, which eventually binds.
- • "Parallel means threads." Three of the four mechanisms involve no threads at all.
- • "More cores will make this faster." Only for core-level parallelism, and only until bandwidth binds.
- • "Vectorising is a kind of multithreading." It is single-threaded and cannot produce a race.
- • "SMT doubles my cores." It adds hardware contexts sharing one core's resources; see SMT: Two Contexts, One Core.
- • "If it did not speed up, the machine has no more parallelism." It may have run out of bandwidth rather than parallelism.
Consequences, controls and cost
- • A threaded implementation of a chain-limited loop multiplies the inefficiency and scales poorly.
- • The cheapest mechanisms require no synchronisation and cannot introduce races.
- • Speedups from combining mechanisms are sublinear because they contend for the same memory system.
- • Problems with irregular access patterns resist the cheap mechanisms entirely and need a data-structure change.
- • Measuring between transformations is necessary, because each one changes which limit binds.
- • Identify the shape of the work before choosing a mechanism — contiguity and uniformity decide what is available.
- • Fix dependency chains first; it is free of risk and improves every subsequent layer.
- • Vectorise next, since it stays single-threaded and cannot race.
- • Thread last, and only for work that is genuinely independent at a coarse grain.
- • Re-measure after each step, because the binding limit moves.
- • Establish scalar single-threaded time as a baseline before applying any mechanism.
- • Measure after each transformation separately so each one's contribution is attributable.
- • Watch IPC and cache-miss counters between steps to see which limit is now binding ([[ipc]]).
- • Test scaling across core counts; a curve that flattens early points at bandwidth rather than at insufficient parallelism.
- • Compare against a bandwidth-saturating kernel on the same machine to know where the ceiling actually is.
- • Each mechanism adds complexity, and the later ones add correctness risk that the earlier ones do not.
- • Vector and threaded code is harder to read, harder to debug and more sensitive to layout changes.
- • Optimising for one machine's core count and vector width reduces portability.
- • Effort spent on the mechanism ladder competes with algorithmic improvement, which usually has a higher ceiling.
Scope
§224 — what these claims are specific to.
- GENERALThe four-way distinction holds on any modern general-purpose machine. What varies is how much of each is available: in-order cores extract little instruction-level parallelism, cores without vector units offer no data-level parallelism, and not all processors implement simultaneous multithreading.
- SIMULATEDThe composition figures are a model of a well-suited numeric kernel, not a measurement. Real speedups depend on the arithmetic intensity of the specific loop and on the machine's bandwidth-to-compute ratio.
Misconceptions
Where the rest of this lives
The two thread-based mechanisms bring synchronisation, races and interleaving reasoning with them. This lesson only places them relative to the cheaper mechanisms; the correctness work belongs with concurrency.