Multicore and Multiprocessor Systems: Symmetric Multiprocessing and Parallel Computing

Multicore and Multiprocessor Systems: Symmetric Multiprocessing and Parallel Computing

Somewhere around the mid-2000s, the computing industry hit a wall. Clock speeds had been climbing for decades, but power density and heat dissipation limits meant that simply cranking frequency higher stopped being viable. The industry’s answer, which has shaped essentially every processor since, was to stop trying to make a single core faster and instead put multiple cores on a single chip. This article covers how multicore and multiprocessor systems work, the symmetric multiprocessing model that underlies most modern designs, and what it actually takes to make software benefit from all those extra cores.

The Historical Turning Point: Why Multicore Happened

Before roughly 2004-2005, performance gains came primarily from three sources: higher clock frequencies, deeper pipelines, and more aggressive instruction-level parallelism techniques (superscalar issue, out-of-order execution, better branch prediction — all covered elsewhere in this series). But this approach ran into what’s often called the power wall.

Dynamic power consumption in CMOS circuits scales roughly according to:

P ≈ C × V² × f

Where C is capacitance, V is voltage, and f is frequency. For years, process shrinks allowed voltage to drop enough to offset frequency increases, keeping power roughly in check. But voltage scaling hit diminishing returns — transistors can only run at so low a voltage before they become unreliable — while clock frequencies kept wanting to climb. The result was that power consumption (and correspondingly, heat) began growing faster than performance gains could justify.

At the same time, architects were hitting the ILP wall: even with wider superscalar issue and larger out-of-order windows, real-world code simply doesn’t contain unlimited exploitable instruction-level parallelism. Doubling execution resources on a single core stopped producing anywhere close to double the performance.

The solution: instead of making one core more complex and power-hungry for shrinking returns, put multiple simpler (or moderately complex) cores on the same die, each running its own independent instruction stream. This trades single-thread performance scaling for thread-level parallelism (TLP) — a completely different, and at the time, mostly untapped, source of parallelism.

Multiprocessor Taxonomy

Before diving into multicore specifically, it helps to place it within the broader landscape of multiprocessor systems.

TypeDescription
SMP (Symmetric Multiprocessing)Multiple identical processors/cores share a single main memory and OS instance, each with equal access to memory and I/O
AMP (Asymmetric Multiprocessing)Processors have different roles or capabilities (e.g., a main CPU plus a dedicated management or I/O processor)
NUMA (Non-Uniform Memory Access)Multiple processors, each with faster access to a local memory region, slower access to others’ memory — common in multi-socket servers
Cluster computingMultiple independent, physically separate computers connected via network, working together on a problem
MulticoreMultiple processing cores integrated onto a single chip, typically operating as SMP

Modern desktop, laptop, and mobile CPUs are essentially all multicore SMP systems on a single die. Large servers often combine multicore chips across multiple sockets, frequently with NUMA memory characteristics.

Symmetric Multiprocessing (SMP) Explained

In an SMP system, every core is functionally identical (or nearly so) and has equal-latency access to shared main memory through a common bus or interconnect. The operating system sees each core as an independent, schedulable processing unit and can freely assign any runnable thread to any core.

Key characteristics of SMP:

A Simplified Multicore Diagram

        +------------------------------------------------+
        |                   CPU Die                       |
        |  +--------+  +--------+  +--------+  +--------+ |
        |  | Core 0 |  | Core 1 |  | Core 2 |  | Core 3 | |
        |  | L1/L2  |  | L1/L2  |  | L1/L2  |  | L1/L2  | |
        |  +--------+  +--------+  +--------+  +--------+ |
        |       \           |           |          /      |
        |        +----------+-----------+---------+       |
        |        |     Shared L3 Cache              |      |
        |        +----------------------------------+      |
        |                     |                             |
        |             Memory Controller                     |
        +------------------------------------------------+
                              |
                        Main Memory (DRAM)

Each core typically has private L1 (and often L2) caches for fast, low-latency access, while a larger shared L3 cache and the memory controller are shared across all cores, providing a common point of coherence and a shared path to main memory.

Cache Coherence: Keeping Everyone’s View Consistent

Cache coherence is one of the trickiest and most important aspects of multicore design. The MESI protocol is the classic foundation:

When one core writes to a cache line that other cores also have cached, coherence hardware (a snooping protocol or, in larger systems, a directory-based protocol) ensures those other copies are invalidated or updated before anyone can read stale data. This all happens transparently to software, but it has real performance implications — heavy “false sharing” (where unrelated variables happen to sit on the same cache line and get bounced between cores) can silently tank multithreaded performance.

Symmetric Multiprocessing vs. NUMA

In small multicore systems, memory access latency is roughly uniform across cores (UMA — Uniform Memory Access). But in larger multi-socket servers, each processor socket typically has its own directly attached memory, and accessing another socket’s memory takes longer because it has to traverse an inter-socket interconnect. This is NUMA (Non-Uniform Memory Access). NUMA-aware software and operating systems try to keep threads and the memory they access on the same socket (“NUMA locality”) to minimize costly cross-socket memory traffic.

How Software Actually Uses Multiple Cores

Having multiple cores available doesn’t automatically make a program faster — software has to be explicitly structured to use them. The main approaches:

Amdahl’s Law: The Fundamental Limit

No discussion of multicore performance is complete without Amdahl’s Law, which quantifies the diminishing returns of adding more cores when a portion of a program must remain sequential:

Speedup(N) = 1 / ( (1 - P) + P/N )

Where P is the fraction of the program that can be parallelized, and N is the number of cores.

If 90% of a program can be parallelized (P = 0.9) but 10% must run sequentially, then even with infinite cores, the maximum possible speedup is only 10x — because that stubborn 10% sequential portion dominates as N grows large.

Cores (N)Speedup if P=0.75Speedup if P=0.95
21.6x1.9x
42.3x3.5x
163.0x8.0x
643.5x13.9x
4.0x20.0x

This table makes clear why real-world software scaling to dozens or hundreds of cores requires an extraordinarily high parallelizable fraction — even a seemingly small 5% sequential bottleneck caps speedup at 20x, no matter how many cores you throw at it.

Real-World Multicore Designs

Performance Considerations

Advantages

Limitations

Common Misconceptions

“More cores always mean better performance.” Only for genuinely parallel, well-optimized workloads. A poorly parallelized program, or an inherently sequential one, may see negligible benefit — or even regress due to added synchronization overhead — from more cores.

“Multicore and multithreading (like Hyper-Threading) are the same thing.” They’re related but distinct — multicore means physically separate execution cores, whereas simultaneous multithreading (covered in the next article in this series) lets a single physical core run multiple threads by sharing execution resources.

“SMP means all cores are literally identical in every implementation.” While the classic SMP model assumes symmetric, identical cores, many modern real-world designs blend the SMP shared-memory model with heterogeneous core types (performance vs. efficiency cores) — the “symmetric” part refers primarily to equal memory/OS visibility, not necessarily identical microarchitecture in every modern chip.

Gustafson’s Law: A More Optimistic Counterpoint

Amdahl’s Law paints a somewhat pessimistic picture of parallel scaling, but it’s worth introducing a complementary perspective: Gustafson’s Law, proposed by John Gustafson in 1988. Amdahl’s Law assumes a fixed problem size and asks how much faster you can solve that same problem with more cores. Gustafson’s Law instead observes that, in practice, when more computing power becomes available, people often choose to solve larger problems rather than solving the same small problem faster — and under that framing, parallel scaling looks considerably more favorable:

Speedup(N) = N - (1 - P) × (N - 1)

Where N is the number of cores and P is the parallelizable fraction, now considered relative to a problem size that scales with available resources rather than staying fixed. Under this model, as long as the sequential portion doesn’t grow proportionally with problem size (a reasonable assumption for many real workloads — larger simulations, bigger datasets, higher-resolution renders), speedup can scale much more favorably with core count than Amdahl’s Law alone would suggest.

The practical takeaway: Amdahl’s Law is the right lens when you’re asking “how much faster can I finish this exact fixed task,” while Gustafson’s Law is the right lens when you’re asking “how much bigger a problem can I tackle in the same amount of time.” Both perspectives are valid and useful depending on the actual question being asked, and real-world discussions of parallel scaling benefit from keeping both in mind rather than treating Amdahl’s Law as the final word on parallel computing’s limits.

Heterogeneous Multicore: Not All Cores Are Created Equal

While the classic SMP model assumes identical, symmetric cores, a major trend in modern multicore design — especially in consumer devices — is heterogeneous multicore, where a single chip combines different types of cores optimized for different purposes. ARM’s big.LITTLE and later DynamIQ architectures pioneered this approach commercially, pairing high-performance cores (larger, more complex, higher power draw) with efficiency cores (smaller, simpler, dramatically lower power draw) on the same chip. Intel’s hybrid architecture (Performance-cores and Efficiency-cores, introduced with Alder Lake) brought a similar concept to mainstream x86 desktop and laptop chips.

This heterogeneous approach connects directly to the DVFS and power management concepts covered elsewhere in this series: rather than relying purely on dynamically scaling a single core type’s voltage and frequency, heterogeneous designs bake fundamentally different power/performance trade-offs directly into distinct core microarchitectures, letting the operating system’s scheduler assign background, lightly-loaded tasks to efficiency cores (saving substantial power) while reserving performance cores for demanding, latency-sensitive, or heavily parallel workloads. This requires OS scheduler cooperation — the scheduler needs to be “hybrid-aware,” correctly identifying which threads benefit most from performance cores versus which can run adequately (and far more efficiently) on efficiency cores.

Real-World Scaling Examples

To ground Amdahl’s and Gustafson’s Laws in something concrete: video encoding is a classic example of a highly parallelizable workload, since different frames (or blocks within frames, depending on the codec and encoding structure) can often be processed largely independently, allowing near-linear scaling across many cores for suitable content and encoding settings. Compiling large software projects also parallelizes well at the file level (many compilation units can be compiled independently before a final linking step, which is typically far more sequential). By contrast, tasks with inherently sequential dependencies — certain cryptographic hash chains, single-threaded legacy application logic, or algorithms with tight, unavoidable data dependencies between steps — see minimal benefit from additional cores no matter how many are made available, which is precisely the scenario Amdahl’s Law describes.

Wrapping Up

Multicore and multiprocessor systems represent the industry’s answer to the physical limits that ended the era of ever-climbing clock speeds. By shifting the burden of finding parallelism from hardware (extracting ILP within a single instruction stream) to software (explicitly structuring programs into independent, parallel tasks), multicore designs unlocked a new axis of scaling — but one that comes with real constraints. Amdahl’s Law reminds us that parallel hardware can only help as much as the software’s structure allows, and cache coherence, memory bandwidth, and synchronization overhead all introduce practical limits well short of theoretical ideals. Understanding SMP, cache coherence, and the fundamentals of parallel scaling is essential to understanding not just how modern CPUs are built, but how to actually write software that takes advantage of them.

Exit mobile version