- Learn
- Computer Science & IT
- Computer Science
- Computer Organization and Architecture
- Parallel Processors and Multicore
Parallel Processors and Multicore
IntermediateLesson 6 of 6 in Computer Organization and Architecture Course
What you will learn
- Explain why processors became parallel, tell instruction-, data-, thread- and request-level parallelism apart, classify machines with Flynn's taxonomy, and describe how hardware multithreading keeps one core busy.
- Explain why computers went parallel and name the kinds of parallelismConnect the power wall with the move to multicore chips, and recognize instruction-, data-, thread- and request-level parallelism in real programs.
- Classify parallel hardware with Flynn's taxonomyUse the number of instruction streams and data streams to place a machine in SISD, SIMD, MISD or MIMD, and explain the SPMD programming style.
- Keep a core busy with hardware multithreadingExplain how fine-grained, coarse-grained and simultaneous multithreading hide stalls, and how hardware threads differ from extra cores.
- Explain why computers went parallel and name the kinds of parallelism
- Calculate speedup and efficiency, apply Amdahl's and Gustafson's laws, account for load imbalance and overhead, and use the roofline model to see whether compute or memory bandwidth limits a program.
- Calculate speedup and efficiency with Amdahl's lawDefine speedup and efficiency, apply Amdahl's law in both directions, and explain why the sequential fraction caps the speedup of a fixed-size problem.
- Distinguish strong and weak scaling and account for overheadsCompare fixed-size and scaled-size speedup, apply Gustafson's law, and estimate the effect of load imbalance, communication and synchronization.
- Bound performance with the roofline modelCompute arithmetic intensity, read a roofline to decide whether a kernel is limited by memory bandwidth or by peak compute, and choose optimizations accordingly.
- Calculate speedup and efficiency with Amdahl's law
- Use SIMD instructions and vector architectures to process arrays, explain how a GPU is organized for throughput, and write and analyze a simple GPU kernel.
- Process several data elements at once with SIMD instructionsWork out how many lanes a SIMD register has, vectorize simple loops, and recognize the data layouts, branches and dependences that limit SIMD speedup.
- Explain how vector architectures run loopsDescribe vector registers, the vector length, strip mining, lanes and strided access, using the RISC-V vector extension as the example.
- Describe how a GPU is organized for throughputExplain streaming multiprocessors, warps and SIMT execution, how GPUs hide memory latency, branch divergence, memory coalescing and the cost of moving data, and how GPUs compare with CPUs and domain-specific accelerators.
- Write and analyze a simple GPU kernelUse the grid, block and thread hierarchy to map data to threads, organize host and device work, and synchronize within a block.
- Process several data elements at once with SIMD instructions
- Describe shared-memory multiprocessors and how threads divide work, explain what cache coherence guarantees, trace snooping and directory protocols, and avoid false sharing.
- Describe shared-memory multiprocessors and divide work among coresExplain a single shared address space, uniform and nonuniform memory access, the cache layout of a multicore chip, and how threads split and combine work.
- Explain the cache coherence problem and what coherence guaranteesShow how private caches can return stale data, state the conditions of a coherent memory system, and tell coherence apart from consistency and from race-free code.
- Trace a snooping protocol with MSI and MESI statesFollow cache block states and bus transactions through a sequence of reads and writes, and explain what the Exclusive state saves.
- Explain directory protocols and avoid false sharingDescribe how a directory tracks sharers, estimate its storage cost, and recognize, measure and fix false sharing.
- Describe shared-memory multiprocessors and divide work among cores
- Describe message-passing clusters and compare interconnection networks by bandwidth and cost, then explain how warehouse-scale computers deliver internet services with attention to cost, energy and failures.
- Describe message-passing clusters and compare interconnection networksExplain how nodes with private memories cooperate by messages, and compare network topologies by total bandwidth, bisection bandwidth and cost.
- Explain how warehouse-scale computers deliver services at scaleDescribe the organization of a warehouse-scale computer and reason about its network, energy use and failures with simple calculations.
- Describe message-passing clusters and compare interconnection networks
Parallel Processors and Multicore study guide
1Find and classify parallel work
Parallel hardware can work on instructions, array elements, threads, or independent requests. The useful choice depends on where a program has work that can run together. Connect that work to the power limits that pushed chips toward multiple cores, then classify the hardware by its instruction streams. Hardware multithreading adds another way to keep a core busy by finding useful instructions during stalls.
1.1 Why chips now share the work
Parallel computers carry out operations at the same time. They can reduce latency, the time one job takes, or increase throughput, the number of jobs finished per second. Those goals lead to different ways of splitting work.
The power wall
Raising the clock rate once made each generation of processors faster. Dynamic power roughly follows : capacitance times voltage squared times switching frequency. Voltage matters twice in that product. Reducing both voltage and frequency by 15% gives of the original dynamic power, about 61%.
Voltage could not keep falling enough to offset higher clock rates. Leakage and heat made continued frequency growth too costly. This power wall pushed the industry toward multicore chips in the mid-2000s. A multicore chip places several processor cores on one chip; a multiprocessor system has at least two processors. More modest cores can deliver more total work per joule, provided there is enough work to share.
Four places to find parallel work
| Kind | What runs together? | Example |
|---|---|---|
| Instruction-level parallelism (ILP) | Instructions from one instruction stream | A pipeline overlaps stages of successive instructions. |
| Data-level parallelism (DLP) | The same operation on many elements | Add corresponding elements of two arrays. |
| Thread-level parallelism (TLP) | Several instruction sequences | Several threads process different image regions. |
| Request-level parallelism | Independent jobs | Servers answer many users' queries at once. |
ILP is found mainly by hardware and compilers. Pipelining, multiple issue, and out-of-order execution overlap instructions while preserving the program's required results. Dependencies and branches limit the available overlap.
DLP appears when many elements need the same calculation. SIMD instructions, vector processors, and GPUs exploit this regularity. TLP requires work to be divided into threads by code, a library, or a compiler. A thread has its own program counter, registers, and stack; threads in one process share its address space. Separate processes have separate address spaces.
Request-level work needs little cooperation between jobs. Adding servers can therefore increase capacity nearly in proportion to the added resources. Running several independent programs on a laptop also uses otherwise idle cores, increasing overall throughput even when none of those programs is parallelized internally.
Concurrent tasks and simultaneous execution
Concurrency means tasks make progress during overlapping periods. One core can do this by taking turns between them. Parallelism means operations execute at the same instant on separate hardware. A single-core system can keep several programs in progress without executing their instructions simultaneously.
A program with one sequential thread does not divide itself across eight cores simply because they exist. The extra cores can run other work, but shortening this program requires parallel work within it. Even after work is split, any remaining sequential part still limits the gain.
Key Takeaways
- Parallel hardware can lower one job’s latency or raise total throughput.
- Dynamic power depends on voltage squared and frequency.
- ILP, DLP, TLP, and request-level parallelism expose different kinds of work.
- Concurrency can use time sharing; parallelism uses simultaneous execution.
1.2 Count instruction and data streams
A processor can contain many arithmetic units without following many independent instruction streams. Flynn's taxonomy classifies hardware by its instruction streams and data streams, so the first step is to choose the level of the machine being described.
The four hardware classes
| Class | Instruction streams | Data streams | Meaning |
|---|---|---|---|
| SISD | Single | Single | One processor follows one ordinary instruction stream. |
| SIMD | Single | Multiple | One instruction applies an operation to several data elements. |
| MISD | Multiple | Single | Different instruction streams operate on one data stream. |
| MIMD | Multiple | Multiple | Processors independently fetch instructions and work on data. |
A conventional single-core processor is SISD even when its pipeline overlaps several instructions. Superscalar issue does not by itself create independent program counters or turn the core into MIMD hardware.
A SIMD instruction might add eight pairs of 32-bit floats in a 256-bit register. It has eight lanes of arithmetic, but they follow one instruction. Fixed-width SIMD extensions include x86 SSE and AVX and Arm Neon; vector architectures also fit the SIMD class. Regular array calculations fit well because many elements need the same operation.
MIMD processors choose their instructions independently. Multicore chips, shared-memory multiprocessors, and clusters fit this class. The cores can run different programs, or copies of the same program that reach different branches at different times.
MISD completes the two-by-two classification. It has few clear practical examples. Some fault-tolerant systems and processing pipelines are described this way, but their classification depends on how their streams are defined.
One program can still have many instruction streams
SPMD, single program, multiple data, is a programming style. Each worker runs a copy of the same program and uses its ID to select its share of the data. Workers have independent execution state, so SPMD commonly runs on MIMD hardware.
For four cores summing 4,000 values, core ID handles indexes through . Core 0 handles 0–999; core 3 handles 3000–3999. Each produces a partial sum, and those four sums are combined. The shared program text does not require all four cores to issue the same instruction in the same cycle.
Classify each level separately
Consider an eight-core CPU whose threads use AVX instructions. Across cores it is MIMD: the cores advance independently. Inside an AVX operation it is SIMD: one instruction controls several lanes. Counting all the arithmetic units together would hide this structure.
A cluster running copies of one scientific program is likewise MIMD hardware using SPMD software. Its nodes communicate through messages. A GPU combines many independently scheduled thread groups with SIMD-like execution inside each group; NVIDIA calls its thread-based execution style SIMT.
To classify a description, ask who chooses the next instruction. Then ask whether that instruction processes one data element or several. The number of source files, programs installed, or arithmetic units alone cannot answer those two questions.
Key Takeaways
- Flynn’s taxonomy counts instruction streams and data streams.
- Pipelining within one instruction stream remains SISD.
- SIMD shares instruction control across lanes; MIMD has independent instruction streams.
- SPMD is a programming style that commonly runs on MIMD hardware.
- One machine can be MIMD across cores and SIMD inside each core.
1.3 Fill idle cycles with another thread
A cache miss can leave a core waiting hundreds of cycles. Hardware multithreading keeps several threads' execution states inside one core so useful instructions from another thread can run during that wait.
What is duplicated and what is shared
Each hardware thread has its own program counter and register state. The threads share the core's execution units, caches, and access to memory bandwidth. Keeping state in hardware makes switching much faster than saving and restoring an operating-system process, which can cost thousands of cycles. Hardware can switch in a cycle or less.
The operating system sees each hardware thread as a logical processor. Eight physical cores with two hardware threads per core expose 16 logical processors. That count describes available thread contexts, not 16 complete physical cores.
Three scheduling approaches
| Approach | When does the core choose another thread? | Main tradeoff |
|---|---|---|
| Fine-grained | Every cycle, skipping stalled threads | Hides short and long stalls, but each thread waits between turns. |
| Coarse-grained | On a long stall, such as a last-level cache miss | Gives one thread long runs, but switching and pipeline refill cost time. |
| Simultaneous multithreading (SMT) | Several threads may issue in the same cycle | Fills unused issue slots while sharing the core's resources. |
Fine-grained scheduling can rotate among ready threads even when the previous thread could continue. Coarse-grained scheduling keeps running that thread until a costly stall justifies switching. SMT uses a multiple-issue, dynamically scheduled core: instructions from different threads can occupy different free issue slots during one cycle. Register renaming keeps their state separate. Intel's Hyper-Threading is an SMT implementation; some IBM POWER processors support eight hardware threads per core.
Work through latency hiding
Suppose a thread computes for 20 cycles and then waits 200 cycles for memory. Its cycle is 220 cycles long. Alone, useful work occupies , or 9.1% of the time.
With four threads, freely staggered work, and no switching cost or added contention, useful work can occupy , or 36.4%. The core still has gaps because four 20-cycle bursts cannot cover the waiting periods.
Eleven threads provide 220 cycles of compute bursts in total. While one waits for 200 cycles, the other ten can each compute for 20. This ideal schedule keeps the core busy. Real performance also depends on whether those threads compete for memory and execution resources.
Throughput and a thread's completion time
Multithreading primarily increases throughput. A particular thread may take longer because other threads use cycles and cache space it would otherwise receive. Two hardware threads sharing one arithmetic unit cannot both gain the capacity of a second full core.
If both threads already demand the same fully occupied execution units, SMT has little spare capacity to fill. If one stalls while the other can execute, sharing is useful. GPUs apply latency hiding at a larger scale by keeping many thread groups ready for their schedulers.
Key Takeaways
- Hardware threads duplicate execution state while sharing a core’s resources.
- Fine-grained switching occurs each cycle; coarse-grained switching responds to long stalls.
- SMT can issue instructions from different threads during one cycle.
- Logical processor counts include hardware thread contexts.
- Latency hiding needs ready work and can be limited by contention.
- Higher throughput need not shorten an individual thread’s run.
2Estimate useful parallel speedup
Extra processors divide only the work that can run in parallel, and they introduce costs of their own. Start with elapsed time and the sequential fraction, then distinguish finishing a fixed job sooner from solving a larger job in the same time. Load balance and coordination explain further losses. The roofline model adds a different limit: whether memory can feed the available arithmetic units quickly enough.
2.1 Calculate the gain and its ceiling
A parallel machine is useful when it changes elapsed time. Speedup compares the best sequential run with the parallel run; efficiency compares that gain with the number of processors used.
Start with measured time
For processors, and . A 100 s sequential job that finishes in 16 s on eight cores has speedup . Its efficiency is , about 78%. Measure wall-clock time for both runs, using the same fixed job.
Divide time into improved and unchanged parts
In Amdahl's law, is the fraction of the original execution time that an enhancement accelerates. It is a time fraction, not a fraction of source-code lines. If that part becomes times faster, then
The unchanged part still takes its full original time. For ideal parallel division across processors, set . This model excludes communication, imbalance, and synchronization overhead; those costs can make measured time longer.
With 90% parallel time and ten processors, normalized time is . Speedup is , and efficiency is about 53%. With 100 processors, time is and speedup is about 9.17. Increasing the processor count tenfold has not doubled the speedup.
Find the limit before buying more processors
As processor count grows, the parallel time approaches zero. The sequential fraction remains, giving the ceiling . A 10% sequential fraction caps ideal speedup at 10; a 5% fraction caps it at 20. Finite processor counts approach these ceilings from below.
Amdahl's law also evaluates one faster instruction class. If multiplication takes 30% of a run and becomes four times faster, normalized time becomes . The whole program is only times faster.
Work backward from a target
To obtain speedup 90 on 100 processors, write . Therefore . Only about 0.11% of the original time can remain sequential. A target close to the processor count demands a very small unchanged fraction.
A measured speedup can sometimes exceed the processor count. Splitting a large working set may make each part fit in cache, reducing the cost per operation. This superlinear speedup changes a premise of the simple model: parallel work is no longer just the original work divided into equal shares. Check the algorithm, data size, and cache behavior before interpreting such a measurement.
Key Takeaways
- Speedup is sequential time divided by parallel time; efficiency is speedup divided by processor count.
- Amdahl’s fraction refers to original execution time.
- Only the improved fraction is divided by the enhancement factor.
- The sequential fraction sets the fixed-workload speedup ceiling.
- Cache effects can change the work’s cost and produce superlinear measured speedup.
2.2 Scale the problem and include the costs
Adding processors can either shorten the same job or let a machine handle a larger job. Strong scaling and weak scaling measure these different uses, and both depend on how much useful work survives the costs of coordination.
Fixed work and growing work
Strong scaling keeps the problem size fixed. Weak scaling grows work in proportion to processor count, keeping work per processor constant. Perfect weak scaling keeps elapsed time constant while total work grows.
Consider ten sequential additions followed by an element-wise sum of two matrices. A 10 by 10 matrix needs 100 parallel additions. Total time is 110 units on one processor, 20 on ten, and 12 on fifty: speedups 5.5 and about 9.2. A 40 by 40 matrix needs 1,600 parallel additions. Times become 1610, 170, and 42 units: speedups about 9.5 and 38.3. The larger problem gives useful parallel work more weight relative to the same ten sequential additions.
Gustafson's different baseline
Gustafson's law uses the sequential time fraction measured on the parallel run. Calling that fraction , scaled speedup is . For and 100 processors, .
Amdahl's law with 5% sequential time on the original one-processor run gives only about 16.8 on 100 processors. These fractions use different denominators and answer different questions. A scaled speedup of 95.05 does not mean the original fixed-size job finished 95.05 times sooner.
The slowest worker sets the finish time
If 400 equal work units go to eight cores, the balanced share is 50 each. Suppose one gets 120 while the other seven get 40 each. The step lasts 120 time units, so speedup is . Dividing by the average load would miss the wait for the busiest core.
Thread creation, scheduling, messages, barriers, and shared-bandwidth contention also consume time. Granularity is the amount of computation between communication or synchronization events. Larger chunks reduce coordination cost per useful operation, but too few chunks can leave some workers idle.
More processors can increase time
For a fully parallel 100 s job with overhead seconds, . Ten processors take 15 s. Fourteen take about 14.14 s, close to the minimum near processors. Forty take 22.5 s because their 20 s overhead outweighs the shorter useful calculation.
When reporting a result, state whether work stayed fixed or grew, and compare with the best sequential program's time. Using a slow parallel implementation running on one processor as the baseline can inflate the reported speedup.
Key Takeaways
- Strong scaling fixes total work; weak scaling fixes work per processor.
- Gustafson’s sequential fraction is measured on the parallel run.
- The slowest worker determines a parallel step’s finish time.
- Communication, synchronization, and contention can grow with processor count.
- Granularity trades coordination cost against load balance.
- Use elapsed time and a best-sequential baseline when reporting speedup.
2.3 Find the compute or memory roof
A processor's advertised arithmetic rate is only one limit. The roofline model also asks whether memory can supply data fast enough to sustain that rate. It gives an upper bound, which helps identify a useful direction for optimization.
Count operations per DRAM byte
Arithmetic intensity is floating-point operations divided by bytes transferred between DRAM and the processor. The original roofline work calls this DRAM-based measure operational intensity. Count the traffic at that boundary, including reads and writes.
DAXPY computes using double-precision numbers. Each element needs one multiplication and one addition: two FLOPs. Reading an 8-byte value from each array and writing 8 bytes back moves 24 bytes. Intensity is FLOPs per byte. The scalar coefficient is reused; it is not a fresh DRAM load for every element in this traffic model.
Apply both limits
If peak compute is and peak memory bandwidth is , the roof is . Bandwidth times FLOPs per byte has units of FLOPs per second. On a log-log graph, the bandwidth bound slopes upward and the compute bound is flat.
For 1,000 GFLOP/s and 100 GB/s, the ridge point is FLOPs per byte. Below this intensity, the bandwidth roof is lower; above it, the compute roof is lower. DAXPY is bounded by GFLOP/s, under 1% of the machine's compute peak. A kernel at intensity 2 is bounded by 200 GFLOP/s.
A memory-bound kernel can still benefit from extra cores or wider SIMD while it has too few requests in flight to use the available bandwidth. Once that bandwidth is saturated, extra arithmetic capacity does not raise its roof. Raising bandwidth or doing more useful computation per transferred byte does.
Reuse changes intensity
Multiplying two 1000 by 1000 double-precision matrices performs about FLOPs. If each element of the two inputs and output crosses the DRAM boundary once, total traffic is 24 MB, giving about 83 FLOPs per byte. That is above the ridge of the example machine.
A naive implementation can reread matrix values repeatedly from DRAM. Blocking organizes loops around submatrices that fit in cache, so values serve many operations before being replaced. The algorithm is still matrix multiplication, but the implementation moves fewer DRAM bytes per operation.
A roof is not a measured result
A kernel can run below the bound because of dependencies, instruction overhead, poor access patterns, or insufficient parallel work. Lower ceilings show such restrictions: lack of SIMD can lower the compute ceiling, while poor access patterns lower usable bandwidth. A point below a roof needs investigation; the roof alone does not predict its exact running rate.
Key Takeaways
- Intensity counts FLOPs per byte at the DRAM boundary.
- The roof is the smaller of peak compute and bandwidth times intensity.
- The ridge point is peak compute divided by peak bandwidth.
- Reuse can increase intensity without changing the mathematical algorithm.
- A roofline gives an upper bound; lower ceilings explain additional restrictions.
3Process arrays with lanes and GPU threads
Arrays offer repeated operations on independent elements, which can be shared across lanes and threads. Follow that idea from fixed-width SIMD registers to variable-length vector loops and then to GPU warps. Each design needs an effective data layout and enough work to fill its hardware. A small kernel shows how array indexes become thread indexes, while transfer costs determine whether offloading helps the complete job.
3.1 Run array operations across SIMD lanes
A SIMD extension divides a wide register into lanes. One instruction performs the same operation in every lane, making regular loops over independent elements a good fit.
Count lanes and iterations
Lanes equal register width divided by element width. A 256-bit register holds eight 32-bit floats, four 64-bit doubles, or 32 one-byte elements. SSE uses 128-bit XMM registers; AVX and AVX2 use 256-bit YMM registers. In 64-bit mode there are 16 YMM registers. AVX-512 provides 32 512-bit ZMM registers, each holding 16 floats. Arm Neon uses 128-bit registers.
For c[i] = a[i] + b[i] over 1,000 floats, eight-lane instructions process the array in 125 vector iterations. Each iteration loads two vectors, adds their lanes, and stores a vector. With 1,003 elements, 125 full iterations leave three elements. A scalar remainder loop or a masked operation handles that short tail without accessing beyond the arrays.
Eight lanes are an arithmetic capacity, not a guarantee of eightfold program speedup. Setup, remainder handling, sequential work, and memory bandwidth still take time.
Choose a layout that fills a register
Simple vector loads work well on contiguous, unit-stride elements. An array of 4-byte {x, y, z} points places consecutive x fields 12 bytes apart. A structure of arrays puts the x values together, then the y values, then the z values. Processing only x can now fill a vector with one contiguous load. Gather and scatter instructions support scattered addresses, but usually at a higher access cost.
Keep conditional work within lanes
For c[i] = (a[i] > 0) ? a[i] : 0, a compare creates a per-lane mask. A blend chooses between candidate results, or masked instructions update only the selected lanes. Inactive lanes retain or receive values according to the instruction's mask behavior.
If different lanes need long, different branches, the instructions for those paths must still be issued, with only the relevant lanes active. The group cannot give each lane a separate instruction stream. A mask preserves correctness but cannot make all those path instructions disappear.
Check iteration independence
Compilers can auto-vectorize loops; programmers can also use libraries or intrinsics such as _mm256_add_ps. The compiler must establish that simultaneous iterations preserve the result. The recurrence a[i] = a[i-1] + b[i] depends on the preceding iteration's new value and cannot be directly vectorized as independent elements. Possible pointer overlap, called aliasing, can also require a run-time overlap check or prevent vectorization.
A reduction keeps several partial sums in a register, then combines lanes. Reordering floating-point additions can change the last bits because addition is not associative. Integer sums agree when the chosen arithmetic has no overflow or uses defined modular arithmetic.
SIMD and multicore execution can combine. Eight cores each issuing one eight-lane add per cycle can perform up to 64 float additions per cycle. They still share limits such as memory bandwidth.
Key Takeaways
- SIMD lane count depends on both register width and element width.
- Remainders need a scalar tail or correctly masked access.
- Contiguous layouts help vector loads use their lanes efficiently.
- Masks handle per-element conditions while sharing instruction issue.
- Dependencies and aliasing can block direct vectorization.
- Floating-point reductions can change rounding when operations are regrouped.
3.2 Let vector length control each loop pass
A vector architecture expresses an operation on a sequence of elements and stores the active vector length in a register. The same program can run on implementations with different vector-register capacities.
Separate capacity, length, and lanes
The RISC-V vector extension, RVV, version 1.0 was ratified in 2021. It provides 32 vector registers, v0 through v31. Each implementation chooses its register width, VLEN, as a power of two. Element width and register grouping determine how many elements fit.
The active length, vl, is the number of elements handled by the next vector instructions. vsetvli configures the vector operation from the remaining element count and the desired element format, returning the selected vl. Code advances by that returned value. This matters because portable code must use the actual length selected by the implementation.
Lanes are the parallel pipelines that execute the vector operation. A 32-element add on four lanes takes about eight cycles after pipeline startup. A 32-element vector does not require 32 arithmetic units. Longer vectors and more lanes are separate design choices.
Strip mine a loop
Strip mining divides a long loop into vector-sized pieces. For 100 elements and a maximum of 32 per operation, one valid sequence of lengths is 32, 32, 32, and 4. Each pass loads the next portion, computes it, stores it, and reduces the remaining count by its vl.
DAXPY over 1,000 doubles can similarly use 31 passes of 32 followed by a pass of eight, for 32 passes in all. RVV can choose other legal lengths near the end, so the loop uses the returned vl instead of assuming a particular final split. Setting the active length handles the short tail without a separate scalar remainder loop.
The loop-control pattern is:
- Request a length for the remaining elements.
- Load and compute that many elements.
- Store the results.
- Advance pointers and subtract the returned length.
One vector instruction describes many element operations, reducing fetch and decode work compared with a scalar body repeated for every element.
Feed dependent operations and noncontiguous data
Chaining lets a vector consumer start on early results before the producer has finished later elements. For a multiply followed by an add, the first product can enter the add pipeline while later products are still being formed.
A mask, commonly in RVV register v0, selects which elements update. A strided load accesses a fixed byte spacing. In a row-major 100 by 100 matrix of doubles, moving down one column advances 100 elements, or 800 bytes. Indexed loads and stores use explicit addresses for irregular data.
Both vector architectures and fixed-width extensions are SIMD in Flynn's taxonomy. Fixed-width extensions encode particular widths; RVV code expresses lengths that adapt to the available hardware. Both still need an execution order that respects the loop's data dependencies.
Key Takeaways
- Vector length says how many elements an operation handles; lanes say how many can advance together.
- RVV code advances by the actual length returned by
vsetvli. - Strip mining processes a long array in vector-sized pieces.
- Chaining overlaps dependent vector operations element by element.
- Masks, strides, and indexed accesses support less regular data.
3.3 Keep a GPU busy with many warps
A CPU concentrates resources on low latency for a few threads. A GPU concentrates resources on high throughput across many data-parallel threads. Choosing between them requires looking at the whole job, including data movement.
Threads run on streaming multiprocessors
A GPU from NVIDIA contains many streaming multiprocessors, or SMs. An SM has arithmetic lanes, warp schedulers, a large register file, and fast on-chip shared memory. A “CUDA core” names an arithmetic lane, not a complete independent CPU-like core. AMD calls the corresponding processing group a compute unit.
NVIDIA groups threads into warps of 32. One issued instruction operates on the warp's active threads, each with its own register values and data. This is SIMT, single instruction, multiple threads. AMD uses wavefronts, with 32 or 64 threads depending on the architecture.
An SM keeps many warps resident. When one waits for memory, the scheduler selects another ready warp. Having more ready threads than lanes hides latency; it does not reduce the latency of an individual DRAM access.
Divergence leaves lanes inactive
If threads in one warp take different branches, the warp executes the required paths with the other threads masked off. An evenly split if-else with equally costly branches can take roughly twice the path time because both paths must execute. A branch can be harmless when its condition is uniform within each warp, even if different warps choose different paths.
Make memory accesses work together
Per-thread registers hold private values. Per-block shared memory is on-chip storage managed by the program. L1 and L2 caches serve automatic reuse, while global memory is the GPU's DRAM, such as GDDR or HBM.
When 32 threads read consecutive 4-byte words, their 128-byte range can be served by a small number of wide transactions. This coalescing uses bandwidth efficiently. Scattered addresses can require many transactions for the same useful data. Shared-memory reuse can further reduce repeated global-memory traffic.
Include the transfer time
For a discrete GPU, inputs and results cross a link such as PCIe or NVLink. Sending 800 MB and receiving 400 MB at an effective 20 GB/s transfers 1.2 GB in 0.06 s, or 60 ms. Adding a 10 ms kernel gives 70 ms overall. A CPU finishing in 50 ms wins this comparison despite the much faster GPU calculation.
Keeping data on the GPU across several kernels avoids repeated transfers. Large, regular jobs can also provide enough useful arithmetic to outweigh movement and launch costs. Small, sequential, branch-heavy jobs often favor a CPU.
Specialize further when the work allows it
A heterogeneous system uses the CPU to run the program and operating system and an accelerator for suitable parts. Domain-specific accelerators such as TPUs and NPUs devote hardware to a narrower task, often matrix multiplication with 8-bit integers or 16-bit floating point. Specialization can improve performance per watt in that domain, while reducing the range of work the hardware handles well.
Key Takeaways
- An SM schedules warps; a CUDA core is an arithmetic lane.
- NVIDIA warps contain 32 threads with separate data and shared instruction issue.
- Resident ready warps hide memory waits.
- Divergence and scattered accesses waste lane time and memory bandwidth.
- Offload decisions include transfers as well as kernel time.
- Specialized accelerators trade flexibility for efficiency in a narrower domain.
3.4 Map array elements to GPU threads
A GPU kernel runs once per launched thread. The host launches a grid of thread blocks, and each thread computes which part of the data it owns. CUDA calls the CPU the host and the GPU the device; OpenCL uses the related terms work-item and work-group.
Calculate a unique index
For a one-dimensional launch, the element index is blockIdx.x * blockDim.x + threadIdx.x. With 256 threads per block, thread 5 in block 2 handles element 517: the two preceding blocks account for 512 elements, and the thread adds an offset of five. Thread and block indexes start at zero.
A SAXPY kernel applies one multiply and one add to each float:
__global__ void saxpy(int n, float a, float *x, float *y) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
y[i] = a * x[i] + y[i];
}
}
The grid replaces the ordinary loop over elements. Each active thread updates its own y[i]; neighboring threads use neighboring addresses, supporting coalescing.
Round the block count up
For 1,000,000 elements and 256 threads per block, the grid needs blocks. These launch 1,000,192 threads. The last 192 have indexes outside the array, so the condition i < n prevents their memory accesses. Rounding down would launch too few threads and leave valid elements untouched.
A block of 256 threads forms eight warps. Blocks can be assigned to any SM and execute in any order. Program correctness must not depend on block 0 finishing before block 1.
Organize host and device work
A typical host allocates device storage, copies the inputs, launches saxpy<<<3907, 256>>>(n, 2.0f, d_x, d_y), and obtains the results. Launch is asynchronous: the CPU can continue while the kernel runs. The host must wait through synchronization or a suitable blocking result copy before it uses the finished data.
Cooperate inside a block
Threads in one block can exchange values through shared memory and meet at __syncthreads(). Every thread participating in that block-wide barrier must reach it. Putting the barrier inside if (i < n) is unsafe when only some threads satisfy the condition.
For a reduction of 1,024 shared-memory values, 512 threads add pairs, then 256 combine the results, then 128, and so on. Ten halving stages leave one sum. A barrier between stages prevents a thread from consuming a partial result before its producer writes it.
An ordinary launch has no grid-wide barrier. Separate blocks can write partial sums and a second kernel can combine them, or appropriate atomic operations can update a global result. A block barrier alone cannot coordinate different blocks. A grid-stride loop is another mapping pattern: each thread handles several elements, advancing by the total grid size.
Key Takeaways
- Compute indexes from block size, block index, and thread index.
- Round block counts up and guard out-of-range accesses.
- Kernel launches are asynchronous with respect to the host.
- Shared memory and
__syncthreads()support cooperation within a block. - Ordinary blocks run independently; combining block results needs another mechanism.
4Share memory and trace cache ownership
A common address space lets threads exchange values through ordinary memory operations. Private caches make those operations fast but create multiple copies that need coordination. Follow a reduction from local work to shared results, then trace the permissions that keep cached copies coherent. Directory storage and false sharing connect those correctness mechanisms to the performance cost of placing data in memory.
4.1 Share an address space and combine partial results
A shared-memory multiprocessor gives its processors one physical address space. Threads communicate with loads and stores to shared variables, while still keeping private values such as local partial sums.
Shared addresses can have different costs
In UMA, uniform memory access, memory access time is roughly the same from every processor. In NUMA, nonuniform memory access, a socket reaches its local memory faster than another socket's memory. The address space is shared even though the physical memory is distributed.
With 70% local accesses at 80 ns and 30% remote accesses at 140 ns, the weighted average is ns. Moving data near its main user reduces the remote share. Counting every access as local would underestimate time.
A common cache layout gives each core private L1 instruction and data caches, often private L2, and a shared last-level cache. A ring or mesh connects the cores and caches. Shared memory describes addressing; it does not require every core to use one common cache.
Split, compute, and reduce
To sum 64,000 values on 64 cores, each core handles 1,000 values. The 64 partial sums can be combined in a reduction tree. The number of active adders halves at each stage: 32, 16, 8, 4, 2, and 1. Six stages combine the partial results.
Using roughly 1,000 local addition steps plus six combining steps gives about 1,006 steps along the longest path, before overhead. The tree avoids making every input update one shared counter. Each stage depends on the previous stage's completed values, so the workers meet at barriers between stages.
OpenMP can express this with #pragma omp parallel for reduction(+:sum). It splits iterations, gives threads private partial sums, and combines them. POSIX threads offer more explicit control over thread management.
Make an update indivisible
An atomic read-modify-write operation makes an update indivisible as observed by other processors. Atomic swap and fetch-and-add are examples. A load followed by an ordinary store is two separate operations, so another thread can intervene.
RISC-V provides lr.w and sc.w. The load-reserved reads a word and establishes a reservation. The store-conditional attempts the write and reports success or failure. An intervening write to the reserved location makes the attempt fail; software retries. The reservation does not block other processors, and failure can also occur for other permitted reasons. These primitives support synchronization mechanisms.
Watch the shared bottlenecks
If each core can stream 15 GB/s but the socket supplies 60 GB/s, about four such streams saturate bandwidth. More cores cannot increase the socket's data rate. Communication through caches also takes work: a writer without write permission must obtain ownership and invalidate other copies. A writer already holding a Modified block can continue locally; later readers may need to fetch a new copy.
Key Takeaways
- Shared memory means one address space, with private data and caches still possible.
- NUMA makes data placement part of performance.
- Local partial sums and a reduction tree limit contention.
- Barriers separate dependent phases of work.
- Atomic operations protect indivisible updates; LR/SC retries after failed attempts.
- Cores share memory bandwidth and pay for ownership transfers.
4.2 Keep copies of one location coherent
Private caches improve latency but can hold several copies of the same memory location. Cache coherence keeps those copies from giving conflicting histories of that location's writes.
Why updating memory is insufficient
Start with X = 0 in memory. Core A reads and caches zero; core B does the same. A writes one into its write-back cache. Without a coherence protocol, B can hit in its own cache and keep reading zero. Writing through to memory would not fix B's already cached copy.
Coherence works at cache-block granularity, commonly 64 bytes. A controller tracks each block's state and follows protocol rules when processors read or write it. Migration moves useful data into the cache of its current user. Replication lets several readers hold clean copies. Coherence must preserve both benefits while controlling writes.
Three required properties
| Property | What it requires |
|---|---|
| Program order for one location | A processor reads its own preceding write if no intervening write replaces it. |
| Write propagation | A write eventually becomes visible to the other processors. |
| Write serialization | All processors agree on the order of writes to that location. |
If a location receives a write of one followed by a write of two, readers cannot disagree about which write came later. Observing the later write and then the earlier write would reverse that history. A fresh subsequent write could of course store the value one again; serialization concerns write events, not whether a numeric value can ever recur.
Control ownership and copies
In a write-invalidate protocol, a writer obtains exclusive write permission and invalidates other cached copies. A later read of an invalidated block misses and fetches current data. This is a coherence miss, in addition to compulsory, capacity, and conflict misses.
A write-update protocol sends new values to existing copies instead. Repeated writes can make this expensive. Snooping broadcasts coherence requests so caches can react. A directory tracks sharers and directs messages to relevant caches.
Coherence, ordering, and atomicity
Coherence concerns one location. A memory consistency model defines allowed observable orderings and results of reads and writes, including operations on different locations. Sequential consistency requires an outcome that matches one interleaving preserving each processor's program order. Relaxed models allow more reorderings; fence instructions enforce required ordering where needed.
Neither coherent copies nor a consistent ordering makes a multi-instruction increment atomic. Suppose two threads increment a shared count initially equal to five. A possible sequence is: A reads five, B reads five, A writes six, B writes six. Every read and write can be coherent, yet the final count is six instead of seven. The program needs an atomic update or suitable synchronization to preserve both increments.
Key Takeaways
- Coherence is needed even when caches write through to memory.
- Coherent systems preserve own-location program order, propagate writes, and serialize writes.
- Write invalidation removes other usable copies before a new owner writes.
- Invalidated data can cause coherence misses.
- Consistency models address ordering across locations.
- A coherent read and a coherent write still form two separate operations.
4.3 Follow MSI and MESI ownership changes
A snooping cache controller watches coherence requests on a broadcast interconnect. To trace a protocol, keep a state for each core's copy of a block and update every affected copy after each access.
Read the states as permissions
| State | Usable copy? | Who else may hold it? | Is memory current? |
|---|---|---|---|
| Modified (M) | Yes, reads and writes | No other valid copy | No |
| Shared (S) | Yes, reads | Other readers may hold copies | Yes |
| Invalid (I) | No | Other caches may hold copies | Depends on their state |
| Exclusive (E), MESI only | Yes, reads and silent transition to writing | No other valid copy | Yes |
In basic MSI, a read miss sends a bus read and loads the block in S. Writing a block in S requires an upgrade to invalidate other copies. Writing from I needs a read-exclusive request to fetch data and obtain ownership. The writer ends in M. A hit in M requires no coherence transaction.
Trace five accesses
Begin with X = 0 in memory and both A and B Invalid.
- A reads X. A bus read supplies zero. States: A = S, B = I.
- B reads X. Another bus read supplies zero. States: A = S, B = S.
- A writes one. Its upgrade invalidates B. States: A = M, B = I.
- B reads X. B misses; A supplies one and downgrades to S. In basic MSI, memory becomes current too. States: A = S, B = S.
- B writes two. Its upgrade invalidates A. States: A = I, B = M.
Each access in this trace requires a bus transaction. The fourth access is the important ownership check: memory alone still had the old value before A supplied its dirty data.
What Exclusive saves
MESI adds E for a single clean cached copy. If A reads an uncached block and no other cache has it, A receives E. A subsequent write changes E to M silently, because there are no other copies to invalidate. MSI would first load S and then send an upgrade for that write.
If another core reads while A holds E, both become S. M and E both have one valid owner, but only M contains data newer than memory.
Repeated writes and scale
A writer that has obtained M can update the block ten times without ten new invalidations. A write-update scheme would send the changed values to sharers.
Broadcast snooping becomes expensive as more cores watch every request. Directory structures reduce that fan-out. More elaborate protocols add useful states: MOESI's Owned state permits a dirty shared copy, and MESIF designates a Forward sharer to answer requests.
Key Takeaways
- MSI tracks Modified, Shared, and Invalid copies.
- A writer needs exclusive ownership; Modified hits stay local.
- A remote read makes a Modified owner supply current data and downgrade in basic MSI.
- MESI’s Exclusive state lets a lone clean reader write without an upgrade transaction.
- Exclusive and Modified both have one owner, but only Modified is dirty.
4.4 Track sharers and separate independent data
Broadcasting every coherence request becomes costly on large machines. A directory records which caches might hold each block so requests can go to those caches. The same block-level tracking also explains a common performance problem: false sharing.
Ask a block's home
Each block has a home directory entry recording whether it is uncached, shared, or modified and which caches hold it. Homes are distributed across nodes or directory slices, spreading storage and request handling.
A read miss goes to the home. If memory has the current data, it can supply them and the directory records the new sharer. If another cache owns a Modified copy, the home forwards the request to that owner so current data can be supplied. In a basic directory protocol, this also makes memory current again.
To write without existing Modified ownership, a core asks the home for permission. The directory sends invalidations to recorded sharers, waits for acknowledgments, and grants exclusive ownership. Repeated write hits in M then need no further coherence messages. Directory indirection adds latency, but point-to-point traffic avoids asking every cache about every block. On-chip snoop filters serve a related purpose by restricting requests to possible holders.
Estimate directory storage
A full presence vector has one bit per core per block. For 64 cores, that is 64 bits, or eight bytes. Relative to a 64-byte data block, presence bits alone cost , or 12.5%. With 256 cores, the 32-byte vector costs 50%. State bits add more. Limited sharer lists, coarser vectors, and tracking only cached blocks reduce this storage burden.
Recognize false sharing
True sharing occurs when cores exchange data through the same word. False sharing occurs when they access different variables within one cache block and at least one writes. The variables are independent in the program, but the coherence unit is larger than either variable.
Two threads increment separate 8-byte counters at 0x1000 and 0x1008. With 64-byte blocks, their block numbers are and . Each ownership change moves permission for both counters together. Frequent alternating writes can make the block bounce between caches even though neither thread needs the other's value.
Moving the second counter to 0x1040 puts it in block 65. Padding or alignment can separate per-thread data; grouping values by their writer also helps. A local accumulator written back once reduces ownership traffic without adding padding to every value.
A writer and a reader of a neighboring variable can also suffer false sharing: the writer invalidates the reader's otherwise useful block. Two readers alone do not cause it. False sharing adds traffic and delay; it does not itself make updates to separate variables produce incorrect values. Measure access patterns and cache-line placement before changing a data layout.
Key Takeaways
- Directories send coherence messages to recorded holders through a block’s home.
- Presence-vector storage grows with both core count and block count.
- False sharing involves different variables in one block and at least one writer.
- Separate ownership by padding, placement, or fewer shared writes.
5Connect nodes and operate at warehouse scale
Beyond one shared-memory machine, nodes exchange messages through a network. A cluster must place data and organize communication as carefully as it divides arithmetic. At warehouse scale, the same network sits beside power, cooling, and routine hardware failures. Compare network capacity with a critical cut, then use energy and latency calculations to judge how a large fleet delivers a reliable service.
5.1 Compare private-memory nodes and their networks
A cluster connects complete computers, each with its own memory and operating system. Nodes cooperate through explicit messages. Data placement and communication cost therefore become part of the program's performance model.
Partition data and combine results
MPI, the Message Passing Interface, gives each process a rank and operations such as send, receive, broadcast, and reduce. An SPMD program can use ranks to select each process's data. A pointer in one node's private address space is not an ordinary shared-memory reference to another node's data.
If one million values are already distributed over 100 nodes, each node sums 10,000 locally. A tree reduction combines the partial sums in seven rounds because . Those rounds involve network messages, not just local additions. Keeping data close to its work reduces transfers.
Clusters scale with standard components, and failed hardware can be replaced. An application may still need recovery or restart after a node fails; replaceable nodes alone do not preserve an interrupted job's progress.
Measure more than link speed
Latency is the time a message takes to arrive. Link bandwidth is one link's data rate. Total network bandwidth sums link capacity and describes an ideal aggregate with all links busy. Bisection bandwidth sums the capacity crossing the lowest-capacity cut that divides nodes into equal halves.
For equal-capacity links of bandwidth , compare 64-node networks using one consistent link-counting convention:
| Topology | Links | Total capacity | Bisection capacity |
|---|---|---|---|
| Ring | 64 | ||
| 8 by 8 mesh | 112 | ||
| 8 by 8 torus | 128 | ||
| 6-dimensional hypercube | 192 | ||
| Fully connected | 2016 |
The mesh has eight rows of seven horizontal links and eight columns of seven vertical links, giving 112. Wrapping both dimensions produces the torus's 128. Each hypercube node has six neighbors, giving links because each link is counted at two endpoints.
Follow the cut, then consider the cost
A ring cut into two halves crosses only two links. Many messages can therefore contend at that boundary despite the ring's 64 links overall. A fully connected network crosses links between its two halves, but needs links in total.
More links can improve bandwidth and reduce hops while increasing switch ports, cables, and power. Shared buses have a single shared communication resource; meshes and tori spread links across space. Large clusters often use multistage switched networks such as fat trees. Their usefulness depends on the traffic pattern as well as their aggregate capacity. For communication between halves of a machine, the bisection exposes a limit that the total hides.
Key Takeaways
- Cluster nodes use private address spaces and explicit communication.
- MPI ranks identify processes; collective operations combine distributed work.
- Message rounds add cost beyond local arithmetic.
- Total bandwidth measures aggregate link capacity; bisection measures a critical cross-machine cut.
- Network capacity trades against ports, cables, power, and hops.
5.2 Measure a data center as one computer
A warehouse-scale computer combines tens of thousands of servers with power, cooling, and a network to deliver internet services. Independent requests provide parallel work, while batch frameworks such as MapReduce split large data-processing jobs across machines.
Match the design to the service
Warehouse-scale systems emphasize cost per request, energy efficiency, and availability. Software retries work, replicates data, and routes around failed machines. A supercomputer more often emphasizes one tightly coupled scientific program and a fast interconnect.
Servers connect to top-of-rack switches, which connect through higher switch levels. Extra levels add communication latency. Uplinks are often oversubscribed, meaning their capacity is below the aggregate capacity of the server links. Some nonblocking Clos-style fabrics provide enough upper capacity to avoid this restriction.
Forty servers with 10 Gbit/s links offer 400 Gbit/s to a rack switch. Four 25 Gbit/s uplinks provide 100 Gbit/s, so the ratio is 4:1. If all servers send off-rack together and share equally, each averages 2.5 Gbit/s. A lightly loaded server can use more when others are idle.
Separate facility overhead from useful computation
Power usage effectiveness, PUE, is total facility energy divided by IT equipment energy over the same period. Ten MW of steady IT load plus 1.5 MW of cooling and distribution overhead gives . The ideal value is 1.0. Google reported an annual fleet-wide PUE of 1.09 for 2025.
PUE measures facility overhead, not useful computations per joule. Idle or inefficient servers can waste energy inside the IT boundary even when the facility has excellent PUE.
For 50,000 servers averaging 300 W, IT power is 15 MW. At PUE 1.2, facility power is 18 MW. Sustained for 8,760 hours, that uses 157,680 MWh; overhead alone uses 26,280 MWh. Power equipment, cooling, electricity, servers, and buildings all contribute to ownership cost.
Low load can still draw substantial power
Energy proportionality means power should rise in proportion to load. A server with 150 W idle power and 300 W peak, increasing linearly, draws W at 30% load. It uses 65% of peak power for 30% of peak work. Historical warehouse-scale measurements found many servers operating between roughly 10% and 50% utilization, making idle power important.
Plan for failures and slow responses
If 50,000 servers each average one failure per ten years, the fleet expects 5,000 failures per year, about 14 per day. That expectation motivates redundancy and retries; it does not schedule exactly 14 failures every day.
A request that needs replies from many servers waits for its slowest required reply. If each of 100 servers independently has a 1% chance of being slow, the chance none is slow is . Thus at least one is slow with probability . Tail latency at one server becomes a common request-level delay when the fan-out is large.
Key Takeaways
- Warehouse-scale design balances service throughput, energy, cost, and availability.
- Oversubscribed uplinks limit simultaneous off-rack traffic.
- PUE measures facility overhead, while server efficiency measures useful work inside IT.
- Idle power makes low utilization costly.
- Large fleets experience routine failures, and fan-out magnifies tail latency.
Parallel Processors and Multicore examples
1Choose a useful source of parallel work
15 steps
- 1IntroductionMore work without a hotter clock
A service must handle more jobs, but increasing its CPU clock would exceed its power budget. Parallel hardware gets more done within a fixed power budget. The designers can add cores (MIMD), widen data operations (SIMD), or hold more threads ready (multithreading). Each choice needs a suitable source of work. Start by identifying what can run together, then ask how that work reaches the hardware and whether the benefit is shorter response time or greater service capacity (throughput).
- 2ExplanationVoltage has a squared effect
Dynamic power roughly follows . With capacitance unchanged, lowering voltage and frequency to 85% multiplies power by . That large reduction helps explain why several modest cores can be attractive. The power wall arose when voltage reductions could no longer offset the heat and power cost of raising clock rates.
- 3ExampleSeparate one request from many requests
If four independent downloads run on four cores, the machine may complete more downloads per second even when each download takes the same time. That is a throughput gain from request-level work. If one image is divided into independent regions and four threads process them, that one image may finish sooner. Parallel hardware serves both goals, but the program must expose the appropriate work.
- 4ExplanationFind work inside a program
A pipeline overlaps instructions from one stream: ILP. An array operation repeats across elements: DLP. Separate threads provide TLP, with their own execution state and a shared process address space. These forms can coexist. A threaded array program can divide regions across cores, use SIMD inside each region, and execute those instructions through each core’s pipeline.
- 5ChallengeDoes taking turns count?
One core alternates between two tasks. Both are in progress for one second, but their instructions never execute at the same instant. Is this parallel execution?
---reveal---
It is concurrent execution through time sharing. Parallelism requires simultaneous operations on separate hardware resources. Another core could provide that simultaneity, but it would still need work ready to execute.
- 6ExplanationAsk who chooses the next instruction
Flynn’s taxonomy starts with instruction and data streams. A conventional scalar single core is SISD. A wide operation that applies one instruction to several elements is SIMD. Independently controlled processors handling their data are MIMD. MISD combines multiple instruction streams with one data stream and has few unambiguous practical examples. Count streams at a chosen hardware level.
- 7ExampleOne program, separate progress
Four workers use their IDs to split a 4,000-element array into four 1,000-element regions. They run the same program, but one can reach its final addition while another is still loading values. This is SPMD software on MIMD hardware. Sharing program text does not require lockstep instruction issue. A SIMD instruction inside any worker would add another level of parallel execution.
- 8ChallengeClassify the chosen level
An independently controlled multicore CPU uses vector additions inside every core. What are the classes across cores and inside a vector addition?
---reveal---
The cores form MIMD hardware because they choose instructions independently. Each vector addition is SIMD because one instruction operates on several data elements. A single machine can have both descriptions when they refer to different levels.
- 9ExplanationKeep execution state ready
A hardware thread context stores a program counter and registers. Several contexts can share one core’s arithmetic units and caches. When a memory request stalls one thread, another ready thread can fill idle execution capacity. The operating system sees each context as a logical processor. Two contexts in one core still share that core’s physical execution resources.
- 10ExplanationChoose when threads share the core
Fine-grained multithreading selects a ready thread each cycle. Coarse-grained multithreading switches after a long stall, paying switch and pipeline-refill costs. SMT goes further on a multiple-issue core: instructions from different threads can issue during the same cycle. All three depend on useful work being ready; none supplies unlimited extra arithmetic capacity.
- 11ExampleFirst measure the idle gap
A thread computes for 20 cycles and then waits 200. Its period is 220 cycles, so active time is . A long wait leaves most cycles unused when no other thread is available. Keep the full compute-plus-wait period in the denominator. Dividing by the wait alone would describe a different ratio, not the fraction of elapsed cycles doing useful work.
- 12ExampleFill the gap with ready bursts
With four freely staggered threads, each contributing 20 compute cycles per 220-cycle period, ideal utilization is . Eleven threads can fill the period: while one waits 200 cycles, the other ten contribute 200 compute cycles. This ideal model has free switching and no added contention; real bandwidth and cache competition can leave less useful work ready.
- 13ChallengeWould another context solve it?
A core’s arithmetic units are already fully occupied, and its two threads both need those same units. Would adding another hardware thread provide the capacity of another core? Explain using the resource they share.
---reveal---
No. A new context supplies another instruction stream but still shares the occupied execution units. Another complete core adds execution hardware. Multithreading is most useful when it fills capacity that stalls or limited ready instructions leave unused.
- 14ConnectionMatch the service improvement to its cause
The service adds hardware contexts and now completes more independent requests per second, while each request’s CPU work takes slightly longer. That result is consistent with improved throughput: ready requests fill idle slots while sharing resources. It would be misleading to call it a single-request latency improvement. Choose a performance measure that matches the work users need completed.
- 15RecapMake the first hardware decision
For a new workload, first identify independent instructions, data elements, threads, or requests. Then locate the hardware level controlling their instruction streams. If the proposed gain comes from extra contexts, inspect which stalls leave capacity available and which resources the contexts share. This gives a concrete basis for choosing more cores, wider operations, or more ready threads before measuring the result.
2Find the limit before adding processors
16 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionThree reasons extra hardware can disappoint
- 2ExplanationChoose the time baseline
- 3ExampleDivide only the parallel portion
- 4Key InsightThe fixed part becomes the whole limit
- 5ChallengeIs the speedup target possible?
- 6ExplanationKeep the workload question visible
- 7ExampleUse the parallel run’s fraction
- 8ExampleWait for the busiest worker
- 9ChallengeWhen does adding cores make time worse?
- 10ExplanationCount the bytes that leave memory
- 11ExampleBuild the two roofs
- 12ExplanationReuse shifts the kernel right
- 13ChallengeWhich upgrade lifts the current bound?
- 14ChallengeA fast kernel inside a slow program
- 15TipMake the reported gain reproducible
- 16RecapChoose the next measurement
3Turn an array loop into GPU work
22 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionStart with independent array elements
- 2ExampleCount elements before instructions
- 3ExplanationPut the values next to each other
- 4ExplanationSelect elements with a mask
- 5ChallengeCan all iterations start together?
- 6ExplanationLet the active length describe the pass
- 7ExampleSplit a long vector into passes
- 8ConnectionFeed one vector operation into another
- 9ChallengeAdvance by what actually ran
- 10ExplanationSchedule a warp while another waits
- 11ExampleDifferent paths waste active lanes
- 12ExplanationMake one transaction serve many threads
- 13ChallengeInclude the trip to the device
- 14ConnectionSpecialize for a narrower workload
- 15ExampleFind one thread’s array element
- 16ExampleCover the final partial block
- 17ExplanationWait before the host uses results
- 18ExampleReduce inside a block
- 19ChallengeCan one block wait for all the others?
- 20Key InsightA reduction changes the arithmetic order
- 21ChallengeCombine a safe mapping with efficient access
- 22RecapCheck the complete array path
4Follow a shared block from reader to writer
19 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionShared variables still need coordination
- 2ExplanationOne address space, different access paths
- 3ExampleFinish local work before combining
- 4ChallengeWhat must survive an intervening write?
- 5ExampleFind the stale cache hit
- 6ExplanationAgree on one location’s history
- 7ChallengeCan coherence preserve both increments?
- 8ConnectionKeep ordering separate from ownership
- 9ExplanationRead the permissions in MSI
- 10ExampleFirst give both cores clean copies
- 11ExampleMove ownership to the writer
- 12Key InsightExclusive removes a private-data request
- 13ChallengeTrace one read and two writes
- 14ExplanationSend the request through the home
- 15ExampleConvert bits before measuring overhead
- 16ExampleFind which bytes share ownership
- 17ChallengeDoes the neighboring reader need to write?
- 18ChallengeMake a correct sum less contentious
- 19RecapInspect both the program and the block
5Follow data, energy, and slow replies across a fleet
14 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionA node is now a complete computer
- 2ExplanationPlace work near its input
- 3ExampleCombine partial sums in message rounds
- 4ExplanationCount the narrow cut
- 5ChallengePay for a wider network
- 6ExplanationShare the uplinks
- 7ExampleInclude cooling and distribution
- 8ExampleTurn steady power into annual energy
- 9ChallengeWhich efficiency did the change improve?
- 10ExampleLow load still consumes power
- 11ExplanationSmall failure rates add across the fleet
- 12ExampleOne slow reply can hold the request
- 13ChallengeWill more servers shorten every request?
- 14RecapChoose a fleet metric with a purpose
Parallel Processors and Multicore practice questions
Set 1: Apply scaling, indexing, and coherence rules10 Practice Questions
Question 1 of 10
Show answerHide answer
Question 2 of 10
Show answerHide answer
Question 3 of 10
Show answerHide answer
Question 4 of 10
Show answerHide answer
Question 5 of 10
Show answerHide answer
Question 6 of 10
Show answerHide answer
Question 7 of 10
Show answerHide answer
Question 8 of 10
Show answerHide answer
Question 9 of 10
Show answerHide answer
Question 10 of 10
Show answerHide answer
Set 2: Recognize parallel work and its costs10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 3: Diagnose performance limits across the system10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
a[i] = a[i-1] + b[i] from left to right. Why can the compiler not directly replace consecutive iterations with independent SIMD lanes?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
vl = 28. The programmer advances both pointers by 32 elements and subtracts 32 from the remaining count. What is the first lost data range?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
0x2040; B repeatedly reads an independent 8-byte status value at 0x2078. Why can B suffer coherence misses?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 4: Combine hardware behavior with performance models10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
if (i < n) { load_into_shared(); __syncthreads(); combine(); }. In its last block only some threads satisfy the condition. What structural correction makes the block barrier safe?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 5: Transfer the models to new design decisions10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
0x3000 and 0x3038 with 64-byte cache blocks. Each has its own writer. What movement separates them into different blocks?Show answerHide answer
Save this lesson to see this answer.
Stuck on your own question? Homework Help
Key Terms
A
Aliasing
Amdahl’s law
Arithmetic intensity
Asynchronous launch
Atomic read-modify-write
Auto-vectorization
Availability
B
Barrier
Bisection bandwidth
Bounds check
Branch divergence
C
Cache coherence
Ceiling
Chaining
Cluster
Coalescing
Coarse-grained multithreading
Coherence miss
Compute-bound kernel
Concurrency
D
Data-level parallelism
Directory
Domain-specific accelerator
E
Energy efficiency
Energy proportionality
Exclusive state
F
False sharing
Fan-out
Fetch-and-add
Fine-grained multithreading
Flynn’s taxonomy
G
Global memory
Granularity
Grid
Gustafson’s law
H
Hardware multithreading
Home node
Host
I
Indexed access
Instruction-level parallelism
Invalid state
K
Kernel
L
Latency hiding
Link bandwidth
Load balance
Logical processor
Loop-carried dependence
M
Mask
Memory consistency model
Memory-bound kernel
MIMD
MISD
Modified state
MPI
N
NUMA
O
Oversubscription
P
Parallel efficiency
Parallel overhead
Parallelism
Power wall
Presence vector
PUE
R
Rank
Reduction tree
Request-level parallelism
Ridge point
Roofline model
S
Sequential consistency
Sequential fraction
Shared memory
Shared state
Shared-memory multiprocessor
SIMD
SIMD lane
SIMT
Simultaneous multithreading
SISD
Snooping
Speedup
SPMD
Streaming multiprocessor
Strided access
Strip mining
Strong scaling
Structure of arrays
Superlinear speedup
T
Tail latency
Thread block
Thread-level parallelism
Torus
True sharing
U
UMA
V
Vector length
VLEN
W
Warehouse-scale computer
Warp
Weak scaling
Write propagation
Write serialization
Write-invalidate protocol
- 1Computer Performance
- 2Instruction Sets and Assembly Language
- 3Computer Arithmetic
- 4Processor Datapath and Pipelining
- 5Memory Hierarchy and Caches
- 6Parallel Processors and Multicore
About this lesson
Make your own Study Materials
Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.