Parallelism is the simultaneous execution of multiple computations on separate hardware resources, such as CPU cores, processors, GPU units or machines, so that a workload finishes sooner or more work is done per second. In programming, it is a specific case within the broader topic of concurrency: concurrency is about structuring a program as tasks that can make progress independently, while parallelism is about those tasks actually running at the same instant.
A single-core machine can run a concurrent program by interleaving tasks, but it cannot run it in parallel. Since processor clock speeds stopped rising quickly in the mid-2000s, adding cores has become the main way hardware delivers more performance, which makes parallelism a core skill for performance-sensitive software.
How parallelism differs from concurrency
The distinction is often summarized as follows: concurrency is about dealing with many things at once, parallelism is about doing many things at once. A web server handling thousands of connections with asynchronous programming on one thread is highly concurrent but not parallel. A matrix multiplication split across 16 cores is parallel even if its structure is simple.
The following table contrasts parallelism with the neighboring concepts it is most often confused with.
| Concept | Core idea | Requires multiple cores | Typical goal |
|---|---|---|---|
| Parallelism | Tasks run at the same instant | Yes | Lower runtime, higher throughput |
| Concurrency | Tasks overlap in time, possibly interleaved | No | Responsiveness, program structure |
| Asynchronous programming | Non-blocking waiting for I/O | No | Handling many I/O operations efficiently |
| Multithreading | Several threads in one process | Not necessarily | Either concurrency or parallelism |
Types of parallelism
Parallelism exists at several levels of the hardware and software stack, and the right level depends on how the work can be divided. Most performance-critical programs combine more than one.
| Form | What runs in parallel | Typical example |
|---|---|---|
| Bit-level and instruction-level | Bits and independent instructions inside one core | Superscalar, out-of-order CPU pipelines |
| SIMD or vector | One instruction on many data elements | AVX instructions processing 8 floats at once |
| Data parallelism | The same operation on different data partitions | Image filters, parallel map over arrays |
| Task parallelism | Different operations at the same time | Parsing, compressing and uploading in parallel |
| Pipeline parallelism | Stages of a process on different items | Video encoding stages, build pipelines |
| Distributed parallelism | Work spread across machines | MapReduce, Spark, MPI clusters |
GPUs push data parallelism to the extreme, running thousands of lightweight threads in lockstep groups. They excel at regular numerical workloads such as machine learning and graphics but perform poorly on branch-heavy, irregular code.
Limits on parallel speedup
Adding cores never gives unlimited speedup. Amdahl’s law states that if a fraction p of a program can run in parallel, the maximum speedup on N cores is 1 / ((1 − p) + p / N). With 95 percent parallel code, even an infinite number of cores yields at most a 20-fold speedup, because the serial 5 percent dominates.
| Parallel fraction | Speedup on 8 cores | Speedup on 64 cores | Upper limit |
|---|---|---|---|
| 50% | 1.8× | 2.0× | 2× |
| 90% | 4.7× | 8.8× | 10× |
| 95% | 5.9× | 15.4× | 20× |
| 99% | 7.5× | 39.3× | 100× |
Gustafson’s law offers a more optimistic view: when the problem size grows with the number of cores, as in scientific simulations, the parallel part grows too and speedup scales better. In practice, speedup is further reduced by overheads: thread creation, synchronization, memory bandwidth limits, load imbalance and false sharing, where cores invalidate each other’s cache lines while writing to neighboring variables.
Programming models for parallel code
Writing raw threads is rarely the best starting point. Higher-level models divide the work and schedule it onto cores automatically, usually with a work-stealing thread pool sized to the number of hardware threads.
- Parallel loops and collections: OpenMP pragmas in C and C++, Java parallel streams, .NET
Parallel.For, Rust Rayon. - Fork-join: a task splits into subtasks, runs them in parallel and joins the results, which suits recursive divide-and-conquer algorithms.
- Process pools: Python
multiprocessingand similar tools run separate processes, sidestepping a global interpreter lock. - Message passing: MPI and actor systems exchange messages instead of sharing memory.
- GPU kernels: CUDA, OpenCL and compute shaders express massively data-parallel work.
A key design choice is shared memory versus message passing. Shared memory is fast but requires locks or atomic operations; message passing avoids shared state at the cost of copying data.
Risks and common mistakes
Parallel code introduces nondeterminism: the order in which threads run changes from one execution to the next. Defects therefore appear intermittently and are hard to reproduce. The table pairs the classic hazards with their remedies.
| Hazard | Cause | Remedy |
|---|---|---|
| Race condition | Unsynchronized access to shared mutable data | Locks, atomics or immutable data |
| Deadlock | Threads wait on each other’s locks in a cycle | Consistent lock ordering, timeouts |
| Load imbalance | Some workers receive far more work | Smaller chunks, work stealing |
| Oversubscription | More busy threads than cores | Size pools to the hardware |
| Excessive granularity | Tasks too small to cover scheduling cost | Batch work into larger units |
The safest rule is to measure before and after parallelizing. Many workloads are limited by memory bandwidth, disk or network rather than CPU, and adding cores to such workloads brings little or no benefit.
The short version
Parallelism means several workers doing parts of a job at the same time. One cook preparing a five-course dinner alone takes hours; five cooks, each handling one course, finish much sooner.
There is a catch. If every course must pass through one shared oven, the cooks wait in line, and adding more cooks barely helps. Computers face the same limit: only the parts of a task that can truly be split gain speed from extra processor cores.
Where it makes a difference
A photo-processing service must generate thumbnails for 200,000 uploaded images every night. Run sequentially on one core, the job takes about six hours and sometimes overlaps with morning traffic.
The developers rewrite the job with a parallel map over a process pool sized to the server’s 16 cores. Each image is independent, so the work divides cleanly, with only the final index file written serially. The job now finishes in about 30 minutes, roughly a 12-fold speedup rather than 16-fold because of disk throughput and the serial step. That result matches what Amdahl’s law predicts and shows why measurement, not core count alone, sets expectations.