Models of parallel processing
Why parallelism
Until the mid-2000s, each new chip generation ran faster mostly because its clock did. When Dennard scaling ended, raising the clock further made chips too hot to cool, and extracting more instruction-level parallelism from one thread hit diminishing returns. Since then, performance growth has come mainly from doing more work in parallel: more data per instruction, more threads per core, more cores per chip and more chips per system.
| Level | What runs in parallel | Who finds it | Example |
|---|---|---|---|
| Instruction-level | Independent instructions from one thread | Hardware and compiler | Pipelining, superscalar issue |
| Data-level | The same operation on many data elements | Compiler or programmer | SIMD instructions, GPUs |
| Thread-level | Separate instruction streams sharing memory | Programmer or runtime | Multicore processors, multithreading |
| Request-level | Independent jobs or requests | Operating system, scheduler | Web servers, cloud data centers |
Flynn's taxonomy
Michael Flynn classified computers in 1966 by how many instruction streams and data streams they process at once. The four classes are still the common vocabulary.
| Single data stream | Multiple data streams | |
|---|---|---|
| Single instruction stream | SISD A classic uniprocessor executing one instruction on one piece of data at a time | SIMD One instruction applied to many data elements: vector units, packed SIMD extensions, GPU warps |
| Multiple instruction streams | MISD Several operations on one data stream. Rare; sometimes used to describe systolic pipelines or redundant fault-tolerant systems | MIMD Independent processors each running their own program on their own data: multicore chips, clusters, supercomputers |
SIMD and vectors
A SIMD instruction operates on a whole register of elements at once. One AVX instruction adds eight 32-bit floats; one AVX-512 instruction adds sixteen. Because only one instruction is fetched and decoded for many operations, SIMD is very energy efficient for regular, data-parallel work such as image processing, audio, linear algebra and machine learning.
for (int i = 0; i < n; i++)
c[i] = a[i] + b[i];for (int i = 0; i < n; i += 8) {
__m256 va = _mm256_loadu_ps(&a[i]);
__m256 vb = _mm256_loadu_ps(&b[i]);
_mm256_storeu_ps(&c[i], _mm256_add_ps(va, vb));
}The AVX version assumes n is a multiple of 8. Compilers usually produce code like this automatically from the scalar loop.
SIMD comes in two styles. Packed SIMD (SSE, AVX, Arm NEON) fixes the register width in the instruction set, so each wider generation needs new instructions. Vector architectures, from the Cray-1 to today's Arm SVE and RISC-V V, let the hardware choose the vector length and handle loop remainders with a vector-length register or masks, so one binary runs on any width. Branches inside a SIMD loop are handled with masks: every lane computes, and a mask decides which results are kept.
GPUs and SIMT
A GPU runs thousands of lightweight threads. Hardware groups them, 32 at a time on NVIDIA GPUs, into a warp that executes one instruction across all its threads: SIMD hardware presented to the programmer as independent threads, a model NVIDIA calls SIMT (single instruction, multiple threads). When threads in a warp take different branches, the warp runs each path in turn with the inactive threads masked off, so divergent code loses efficiency.
GPUs hide memory latency by switching among many warps rather than by large caches and out-of-order execution. They suit problems with massive, regular parallelism, which is why they dominate machine-learning training and most of the world's fastest supercomputers.
MIMD
| Shared memory | Distributed memory | |
|---|---|---|
| Hardware | Multicore chips and multi-socket servers; one address space | Clusters of separate computers linked by a network |
| Memory access | UMA (uniform access, as in a single chip) or NUMA (faster to local memory than to another socket's) | Each node reaches only its own memory directly |
| Communication | Loads and stores to shared variables | Explicit messages |
| Programming | Threads, OpenMP | MPI |
| Main challenges | Coherence, synchronization, races | Communication cost, data distribution |
| Scale | Up to hundreds of cores | Up to millions of cores |
Shared-memory parallelism can be very concise. OpenMP splits this loop across all cores and combines each core's partial sum:
#pragma omp parallel for reduction(+:sum)
for (long i = 0; i < n; i++)
sum += a[i];
Other models
| Model | Idea | Where it appears |
|---|---|---|
| SPMD | Single program, multiple data: every processor runs the same program on its own part of the data | Most MPI and GPU programs; a programming style on MIMD hardware |
| Multithreading | One core interleaves several threads to fill idle cycles: fine-grained (switch every cycle), coarse-grained (switch on long stalls) or simultaneous (SMT: issue from several threads in the same cycle) | Intel Hyper-Threading, AMD SMT, IBM POWER (up to 8 threads per core) |
| Systolic arrays | A grid of simple cells, each passing data to its neighbors in rhythm, so each value is reused many times without returning to memory (Kung) | Google TPU matrix units, signal processing |
| Dataflow | An instruction runs as soon as its inputs arrive, with no program counter | Research machines; the idea lives on inside out-of-order cores and some accelerators |
| Heterogeneous | Different kinds of cores on one chip, each suited to part of the work | Phone and laptop chips with performance and efficiency cores, GPUs and neural engines |
Multicore processors
Inside a multicore chip
Olukotun and colleagues argued in 1996 that several simple cores on one chip would beat one ever more complex core, and by the mid-2000s every major vendor had followed. A typical multicore processor has:
Key design choices include how many cores to build and how large to make each one, how much cache to share, the interconnect topology, and whether to split the design into chiplets: several smaller dies in one package, which improves manufacturing yield at the cost of slower links between dies.
Cache coherence
When two cores cache the same memory block, a write by one must not leave the other reading a stale copy. A coherence protocol guarantees that every core sees writes to the same location in a single order, and eventually sees the latest value. Most protocols are write-invalidate: before a core writes, every other copy is invalidated.
Small systems use snooping: every cache watches a shared bus or broadcast network for other cores' requests. Large systems use a directory (Censier and Feautrier) that records which caches hold each block, so requests go only to those caches instead of to everyone.
| MESI state | Meaning | Can read | Can write without a bus request |
|---|---|---|---|
| M Modified | Only copy, and dirty: memory is out of date | Yes | Yes |
| E Exclusive | Only copy, clean | Yes | Yes, silently becoming M |
| S Shared | One of possibly several clean copies | Yes | No: must invalidate the others first |
| I Invalid | No valid copy | No: must fetch | No: must fetch and invalidate |
The Exclusive state, from Papamarcos and Patel's protocol, saves a bus transaction in the common case of reading private data and then writing it.
MESI simulator
Follow one memory block through a snooping MESI protocol. Enter operations as R (read), W (write) or E (evict) followed by a core number, such as R0 W1 E0.
Things to try. "Read sharing" needs one bus request per core and then runs without traffic. In "Read, then write", core 0's second write is free because Exclusive upgrades silently. "Two cores writing in turn" generates a bus transaction and a transfer of the whole block on every single write, even if the cores write different variables that happen to share a block.
Memory consistency
Coherence is about one location. Consistency is about the order in which a core's accesses to different locations become visible to other cores. Lamport defined sequential consistency: the result is as if all operations ran in some single interleaving that respects each program's order. Real hardware relaxes this for speed, mainly by letting stores wait in a store buffer while later loads go ahead.
| Core 0 | Core 1 | Can both r1 and r2 be 0? |
|---|---|---|
| x = 1 r1 = y | y = 1 r2 = x | Sequential consistency: no. x86 (TSO), Arm, RISC-V: yes, because each core's store can still be in its store buffer when its load reads memory. A fence between the store and the load forbids it. |
x and y start at 0. Adve and Gharachorloo's tutorial explains the common models; tools like herd7 enumerate exactly which outcomes each model allows.
Interconnects
| Topology | Average distance (p nodes) | Bandwidth as p grows | Cost | Used for |
|---|---|---|---|---|
| Shared bus | 1 hop | Fixed: every node shares one bus | Lowest | Early multiprocessors, a few cores |
| Ring | About p/4 hops (bidirectional) | Grows slowly | Low | Client CPUs with roughly 4–12 cores or slices |
| 2D mesh | About 2√p/3 hops | Grows with √p | Moderate | Server CPUs with dozens of cores |
| Crossbar | 1 hop | Grows with p | Grows with p², so only for small p | Connecting a few clusters or cache banks |
Scalability and performance
Scaling laws
Amdahl's law assumes a fixed problem: if a fraction f of the work runs in parallel, the serial remainder caps the speedup at 1 / (1 − f), however many processors are added. With f = 0.95, 1,024 cores give only about 20×. Measuring this way is called strong scaling.
Gustafson's law assumes the problem grows with the machine, as with a bigger simulation mesh on a bigger computer, so the parallel part grows while the serial part does not. Measuring this way is called weak scaling, and it explains why supercomputers with millions of cores are useful. Neither law is wrong; they answer different questions.
Parallel efficiency is speedup divided by processor count. The Karp–Flatt metric works backward from a measured speedup S on p processors to the experimentally determined serial fraction e = (1/S − 1/p) / (1 − 1/p). If e grows as p grows, overhead rather than inherent serial work is limiting scaling.
Scalability explorer
Compare the two laws with a more realistic model that adds a coordination cost growing with the logarithm of the processor count, as tree-structured synchronization and reductions do.
What limits scaling
| Limit | Why it grows with processors | Mitigation |
|---|---|---|
| Serial code | Its share of run time rises as the parallel part shrinks | Parallelize more; grow the problem (weak scaling) |
| Synchronization | Locks and barriers make cores wait for the slowest | Finer-grained or lock-free algorithms; fewer barriers |
| Communication | More boundaries between pieces of the problem; longer distances | Partition to keep data local; overlap communication with computation |
| Load imbalance | Smaller pieces vary more in cost | Dynamic scheduling, work stealing |
| Memory bandwidth | Cores multiply, but DRAM channels do not keep pace | Cache blocking; more data reuse per byte (the roofline model) |
| Coherence traffic | More sharers, more invalidations, false sharing | Avoid sharing writable data; padding |
| Power | Not every core can run at full speed at once ("dark silicon") | Specialized and heterogeneous cores; lower clock under heavy load |
Designing the chip
Hill and Marty applied Amdahl's law to a fixed chip budget of n "base core equivalents" (BCEs). Combining r BCEs makes one bigger core with performance √r, following Pollack's rule that performance grows roughly with the square root of core area. Three organizations follow:
- Symmetric: n/r identical cores of size r. Big cores speed up serial code but leave fewer cores for parallel code.
- Asymmetric: one big core of size r plus n − r small cores. The big core runs serial code; all cores share the parallel part.
- Dynamic: resources that act as one big core for serial code and as n small cores for parallel code. An idealized upper bound.
Multicore design calculator
Things to try. With the defaults, the best symmetric chip uses cores of about 8 BCEs and reaches about 51×; the best asymmetric chip pairs one 64-BCE core with 192 small ones for about 125×. Lower f to 0.9 and the best designs shift to much bigger cores; raise it to 0.999 and many small cores win. That is the case for heterogeneous chips: a few big cores for serial code, many small ones for parallel throughput.
Comparing techniques
| Technique | Granularity | Scales to | Communication cost | Energy efficiency | Programming effort | Best for |
|---|---|---|---|---|---|---|
| SIMD units | Single instructions | 4–64 lanes | None: registers | Very high | Low with auto-vectorization; higher with intrinsics | Regular loops over arrays |
| GPU (SIMT) | Thousands of threads | Tens of thousands of lanes per GPU | Low on-chip; high to the host | High for regular work | Moderate to high | Massive, regular data parallelism |
| SMT | Threads on one core | 2–8 threads per core | None: shared caches | High when threads stall often | None beyond threading | Latency-bound, mixed workloads |
| Shared-memory multicore | Threads | Up to hundreds of cores | Coherence traffic | Moderate | Moderate; races and locks are hard | General-purpose parallel programs |
| Distributed cluster | Processes | Millions of cores | High: network messages | Depends on locality | High: explicit data distribution | Very large problems, weak scaling |
Real systems combine all of them: a supercomputer node runs MPI processes across nodes, threads across cores, SIMD within each core and GPU kernels on accelerators. Choosing well means matching each level of the hardware to a level of parallelism the problem actually has.
Resources
Interactive tools and references
Peer-reviewed reading
- Flynn, M. J. (1966). Very high-speed computing systems. Proceedings of the IEEE, 54(12), 1901–1909.The origin of SISD, SIMD, MISD and MIMD.doi.org/10.1109/PROC.1966.5273
- Russell, R. M. (1978). The CRAY-1 computer system. Communications of the ACM, 21(1), 63–72.The classic vector supercomputer.doi.org/10.1145/359327.359336
- Kung, H. T. (1982). Why systolic architectures? IEEE Computer, 15(1), 37–46.The idea behind today's matrix accelerators.doi.org/10.1109/MC.1982.1653825
- Lindholm, E., Nickolls, J., Oberman, S., & Montrym, J. (2008). NVIDIA Tesla: A unified graphics and computing architecture. IEEE Micro, 28(2), 39–55.The architecture that introduced SIMT computing on GPUs.doi.org/10.1109/MM.2008.31
- Nickolls, J., & Dally, W. J. (2010). The GPU computing era. IEEE Micro, 30(2), 56–69.How GPUs became general-purpose parallel processors.doi.org/10.1109/MM.2010.41
- Olukotun, K., et al. (1996). The case for a single-chip multiprocessor. Proc. ASPLOS VII, 2–11.Why several simple cores beat one complex core.doi.org/10.1145/248209.237140
- Tullsen, D. M., Eggers, S. J., & Levy, H. M. (1995). Simultaneous multithreading: Maximizing on-chip parallelism. Proc. ISCA, 392–403.Thread-level parallelism inside one core.doi.org/10.1109/ISCA.1995.524578
- Kumar, R., et al. (2003). Single-ISA heterogeneous multi-core architectures: The potential for processor power reduction. Proc. MICRO-36, 81–92.The research behind performance and efficiency cores.doi.org/10.1109/MICRO.2003.1253185
- Censier, L. M., & Feautrier, P. (1978). A new solution to coherence problems in multicache systems. IEEE Transactions on Computers, C-27(12), 1112–1118.Directory-based coherence.doi.org/10.1109/TC.1978.1675013
- Papamarcos, M. S., & Patel, J. H. (1984). A low-overhead coherence solution for multiprocessors with private cache memories. Proc. ISCA, 348–354.The protocol that became MESI.doi.org/10.1145/800015.808204
- Torrellas, J., Lam, M. S., & Hennessy, J. L. (1994). False sharing and spatial locality in multiprocessor caches. IEEE Transactions on Computers, 43(6), 651–663.How block size creates false sharing.doi.org/10.1109/12.286299
- Lamport, L. (1979). How to make a multiprocessor computer that correctly executes multiprocess programs. IEEE Transactions on Computers, C-28(9), 690–691.The definition of sequential consistency.doi.org/10.1109/TC.1979.1675439
- Adve, S. V., & Gharachorloo, K. (1996). Shared memory consistency models: A tutorial. IEEE Computer, 29(12), 66–76.The standard introduction to relaxed memory models.doi.org/10.1109/2.546611
- Dally, W. J. (1990). Performance analysis of k-ary n-cube interconnection networks. IEEE Transactions on Computers, 39(6), 775–785.Why low-dimensional meshes and tori suit real chips and machines.doi.org/10.1109/12.53599
- Amdahl, G. M. (1967). Validity of the single processor approach to achieving large scale computing capabilities. Proc. AFIPS Spring Joint Computer Conference, 483–485.The original statement of Amdahl's law.doi.org/10.1145/1465482.1465560
- Gustafson, J. L. (1988). Reevaluating Amdahl's law. Communications of the ACM, 31(5), 532–533.Scaled speedup when the problem grows with the machine.doi.org/10.1145/42411.42415
- Karp, A. H., & Flatt, H. P. (1990). Measuring parallel processor performance. Communications of the ACM, 33(5), 539–543.Estimating the serial fraction from measured speedups.doi.org/10.1145/78607.78614
- Hill, M. D., & Marty, M. R. (2008). Amdahl's law in the multicore era. IEEE Computer, 41(7), 33–38.The model behind the multicore design calculator.doi.org/10.1109/MC.2008.209
- Esmaeilzadeh, H., et al. (2011). Dark silicon and the end of multicore scaling. Proc. ISCA, 365–376.Why power limits how many cores can run at once.doi.org/10.1145/2000064.2000108