Recent from talks
Nothing was collected or created yet.
Parallel computing
View on Wikipedia
Parallel computing is a type of computation in which many calculations or processes are carried out simultaneously.[1] Large problems can often be divided into smaller ones, which can then be solved at the same time. There are several different forms of parallel computing: bit-level, instruction-level, data, and task parallelism. Parallelism has long been employed in high-performance computing, but has gained broader interest due to the physical constraints preventing frequency scaling.[2] As power consumption (and consequently heat generation) by computers has become a concern in recent years,[3] parallel computing has become the dominant paradigm in computer architecture, mainly in the form of multi-core processors.[4]

In computer science, parallelism and concurrency are two different things: a parallel program uses multiple CPU cores, each core performing a task independently. On the other hand, concurrency enables a program to deal with multiple tasks even on a single CPU core; the core switches between tasks (i.e. threads) without necessarily completing each one. A program can have both, neither or a combination of parallelism and concurrency characteristics.[5]
Parallel computers can be roughly classified according to the level at which the hardware supports parallelism, with multi-core and multi-processor computers having multiple processing elements within a single machine, while clusters, MPPs, and grids use multiple computers to work on the same task. Specialized parallel computer architectures are sometimes used alongside traditional processors, for accelerating specific tasks.
In some cases parallelism is transparent to the programmer, such as in bit-level or instruction-level parallelism, but explicitly parallel algorithms, particularly those that use concurrency, are more difficult to write than sequential ones,[6] because concurrency introduces several new classes of potential software bugs, of which race conditions are the most common. Communication and synchronization between the different subtasks are typically some of the greatest obstacles to getting optimal parallel program performance.
A theoretical upper bound on the speed-up of a single program as a result of parallelization is given by Amdahl's law, which states that it is limited by the fraction of time for which the parallelization can be utilised.
Background
[edit]Traditionally, computer software has been written for serial computation. To solve a problem, an algorithm is constructed and implemented as a serial stream of instructions. These instructions are executed on a central processing unit on one computer. Only one instruction may execute at a time—after that instruction is finished, the next one is executed.[7]
Parallel computing, on the other hand, uses multiple processing elements simultaneously to solve a problem. This is accomplished by breaking the problem into independent parts so that each processing element can execute its part of the algorithm simultaneously with the others. The processing elements can be diverse and include resources such as a single computer with multiple processors, several networked computers, specialized hardware, or any combination of the above.[7] Historically parallel computing was used for scientific computing and the simulation of scientific problems, particularly in the natural and engineering sciences, such as meteorology. This led to the design of parallel hardware and software, as well as high performance computing.[8]
Frequency scaling was the dominant reason for improvements in computer performance from the mid-1980s until 2004. The runtime of a program is equal to the number of instructions multiplied by the average time per instruction. Maintaining everything else constant, increasing the clock frequency decreases the average time it takes to execute an instruction. An increase in frequency thus decreases runtime for all compute-bound programs.[9] However, power consumption P by a chip is given by the equation P = C × V 2 × F, where C is the capacitance being switched per clock cycle (proportional to the number of transistors whose inputs change), V is voltage, and F is the processor frequency (cycles per second).[10] Increases in frequency increase the amount of power used in a processor. Increasing processor power consumption led ultimately to Intel's May 8, 2004 cancellation of its Tejas and Jayhawk processors, which is generally cited as the end of frequency scaling as the dominant computer architecture paradigm.[11]
To deal with the problem of power consumption and overheating the major central processing unit (CPU or processor) manufacturers started to produce power efficient processors with multiple cores. The core is the computing unit of the processor and in multi-core processors each core is independent and can access the same memory concurrently. Multi-core processors have brought parallel computing to desktop computers. Thus parallelization of serial programs has become a mainstream programming task. In 2012 quad-core processors became standard for desktop computers, while servers had 10+ core processors. By 2023 some processors had over hundred cores. Some designs having a mix of performance and efficiency cores (such as ARM's big.LITTLE design) due to thermal and design constraints.[12][citation needed] From Moore's law it can be predicted that the number of cores per processor will double every 18–24 months.
An operating system can ensure that different tasks and user programs are run in parallel on the available cores. However, for a serial software program to take full advantage of the multi-core architecture the programmer needs to restructure and parallelize the code. A speed-up of application software runtime will no longer be achieved through frequency scaling, instead programmers will need to parallelize their software code to take advantage of the increasing computing power of multicore architectures.[13]
Relevant laws
[edit]

Main article: Amdahl's law
Optimally, the speedup from parallelization would be linear—doubling the number of processing elements should halve the runtime, and doubling it a second time should again halve the runtime. However, very few parallel algorithms achieve optimal speedup. Most of them have a near-linear speedup for small numbers of processing elements, which flattens out into a constant value for large numbers of processing elements.
The maximum potential speedup of an overall system can be calculated by Amdahl's law.[14] Amdahl's Law indicates that optimal performance improvement is achieved by balancing enhancements to both parallelizable and non-parallelizable components of a task. Furthermore, it reveals that increasing the number of processors yields diminishing returns, with negligible speedup gains beyond a certain point.[15][16]
Amdahl's Law has limitations, including assumptions of fixed workload, neglecting inter-process communication and synchronization overheads, primarily focusing on computational aspect and ignoring extrinsic factors such as data persistence, I/O operations, and memory access overheads.[17][18][19]
Gustafson's law and Universal Scalability Law give a more realistic assessment of the parallel performance.[20][21]

Dependencies
[edit]Understanding data dependencies is fundamental in implementing parallel algorithms. No program can run more quickly than the longest chain of dependent calculations (known as the critical path), since calculations that depend upon prior calculations in the chain must be executed in order. However, most algorithms do not consist of just a long chain of dependent calculations; there are usually opportunities to execute independent calculations in parallel.
Let Pi and Pj be two program segments. Bernstein's conditions[22] describe when the two are independent and can be executed in parallel. For Pi, let Ii be all of the input variables and Oi the output variables, and likewise for Pj. Pi and Pj are independent if they satisfy
Violation of the first condition introduces a flow dependency, corresponding to the first segment producing a result used by the second segment. The second condition represents an anti-dependency, when the second segment produces a variable needed by the first segment. The third and final condition represents an output dependency: when two segments write to the same location, the result comes from the logically last executed segment.[23]
Consider the following functions, which demonstrate several kinds of dependencies:
1: function Dep(a, b) 2: c := a * b 3: d := 3 * c 4: end function
In this example, instruction 3 cannot be executed before (or even in parallel with) instruction 2, because instruction 3 uses a result from instruction 2. It violates condition 1, and thus introduces a flow dependency.
1: function NoDep(a, b) 2: c := a * b 3: d := 3 * b 4: e := a + b 5: end function
In this example, there are no dependencies between the instructions, so they can all be run in parallel.
Bernstein's conditions do not allow memory to be shared between different processes. For that, some means of enforcing an ordering between accesses is necessary, such as semaphores, barriers or some other synchronization method.
Race conditions, mutual exclusion, synchronization, and parallel slowdown
[edit]Subtasks in a parallel program are often called threads. Some parallel computer architectures use smaller, lightweight versions of threads known as fibers, while others use bigger versions known as processes. However, "threads" is generally accepted as a generic term for subtasks.[24] Threads will often need synchronized access to an object or other resource, for example when they must update a variable that is shared between them. Without synchronization, the instructions between the two threads may be interleaved in any order. For example, consider the following program:
| Thread A | Thread B |
|---|---|
| 1A: Read variable V | 1B: Read variable V |
| 2A: Add 1 to variable V | 2B: Add 1 to variable V |
| 3A: Write back to variable V | 3B: Write back to variable V |
If instruction 1B is executed between 1A and 3A, or if instruction 1A is executed between 1B and 3B, the program will produce incorrect data. This is known as a race condition. The programmer must use a lock to provide mutual exclusion. A lock is a programming language construct that allows one thread to take control of a variable and prevent other threads from reading or writing it, until that variable is unlocked. The thread holding the lock is free to execute its critical section (the section of a program that requires exclusive access to some variable), and to unlock the data when it is finished. Therefore, to guarantee correct program execution, the above program can be rewritten to use locks:
| Thread A | Thread B |
|---|---|
| 1A: Lock variable V | 1B: Lock variable V |
| 2A: Read variable V | 2B: Read variable V |
| 3A: Add 1 to variable V | 3B: Add 1 to variable V |
| 4A: Write back to variable V | 4B: Write back to variable V |
| 5A: Unlock variable V | 5B: Unlock variable V |
One thread will successfully lock variable V, while the other thread will be locked out—unable to proceed until V is unlocked again. This guarantees correct execution of the program. Locks may be necessary to ensure correct program execution when threads must serialize access to resources, but their use can greatly slow a program and may affect its reliability.[25]
Locking multiple variables using non-atomic locks introduces the possibility of program deadlock. An atomic lock locks multiple variables all at once. If it cannot lock all of them, it does not lock any of them. If two threads each need to lock the same two variables using non-atomic locks, it is possible that one thread will lock one of them and the second thread will lock the second variable. In such a case, neither thread can complete, and deadlock results.[26]
Many parallel programs require that their subtasks act in synchrony. This requires the use of a barrier. Barriers are typically implemented using a lock or a semaphore.[27] One class of algorithms, known as lock-free and wait-free algorithms, altogether avoids the use of locks and barriers. However, this approach is generally difficult to implement and requires correctly designed data structures.[28]
Not all parallelization results in speed-up. Generally, as a task is split up into more and more threads, those threads spend an ever-increasing portion of their time communicating with each other or waiting on each other for access to resources.[29][30] Once the overhead from resource contention or communication dominates the time spent on other computation, further parallelization (that is, splitting the workload over even more threads) increases rather than decreases the amount of time required to finish. This problem, known as parallel slowdown,[31] can be improved in some cases by software analysis and redesign.[32]
Fine-grained, coarse-grained, and embarrassing parallelism
[edit]Applications are often classified according to how often their subtasks need to synchronize or communicate with each other. An application exhibits fine-grained parallelism if its subtasks must communicate many times per second; it exhibits coarse-grained parallelism if they do not communicate many times per second, and it exhibits embarrassing parallelism if they rarely or never have to communicate. Embarrassingly parallel applications are considered the easiest to parallelize.
Flynn's taxonomy
[edit]Michael J. Flynn created one of the earliest classification systems for parallel (and sequential) computers and programs, now known as Flynn's taxonomy. Flynn classified programs and computers by whether they were operating using a single set or multiple sets of instructions, and whether or not those instructions were using a single set or multiple sets of data.
| Flynn's taxonomy |
|---|
| Single data stream |
| Multiple data streams |
| SIMD subcategories[33] |
| See also |
The single-instruction-single-data (SISD) classification is equivalent to an entirely sequential program. The single-instruction-multiple-data (SIMD) classification is analogous to doing the same operation repeatedly over a large data set. This is commonly done in signal processing applications. Multiple-instruction-single-data (MISD) is a rarely used classification. While computer architectures to deal with this were devised (such as systolic arrays), few applications that fit this class materialized. Multiple-instruction-multiple-data (MIMD) programs are by far the most common type of parallel programs.
According to David A. Patterson and John L. Hennessy, "Some machines are hybrids of these categories, of course, but this classic model has survived because it is simple, easy to understand, and gives a good first approximation. It is also—perhaps because of its understandability—the most widely used scheme."[34]
Disadvantages
[edit]Parallel computing can incur significant overhead in practice, primarily due to the costs associated with merging data from multiple processes. Specifically, inter-process communication and synchronization can lead to overheads that are substantially higher—often by two or more orders of magnitude—compared to processing the same data on a single thread.[35][36][37] Therefore, the overall improvement should be carefully evaluated.
Granularity
[edit]Bit-level parallelism
[edit]
From the advent of very-large-scale integration (VLSI) computer-chip fabrication technology in the 1970s until about 1986, speed-up in computer architecture was driven by doubling computer word size—the amount of information the processor can manipulate per cycle.[38] Increasing the word size reduces the number of instructions the processor must execute to perform an operation on variables whose sizes are greater than the length of the word. For example, where an 8-bit processor must add two 16-bit integers, the processor must first add the 8 lower-order bits from each integer using the standard addition instruction, then add the 8 higher-order bits using an add-with-carry instruction and the carry bit from the lower order addition; thus, an 8-bit processor requires two instructions to complete a single operation, where a 16-bit processor would be able to complete the operation with a single instruction.
Historically, 4-bit microprocessors were replaced with 8-bit, then 16-bit, then 32-bit microprocessors. This trend generally came to an end with the introduction of 32-bit processors, which has been a standard in general-purpose computing for two decades. Not until the early 2000s, with the advent of x86-64 architectures, did 64-bit processors become commonplace.
Instruction-level parallelism
[edit]
A computer program is, in essence, a stream of instructions executed by a processor. Without instruction-level parallelism, a processor can only issue less than one instruction per clock cycle (IPC < 1). These processors are known as subscalar processors. These instructions can be re-ordered and combined into groups which are then executed in parallel without changing the result of the program. This is known as instruction-level parallelism. Advances in instruction-level parallelism dominated computer architecture from the mid-1980s until the mid-1990s.[39]

All modern processors have multi-stage instruction pipelines. Each stage in the pipeline corresponds to a different action the processor performs on that instruction in that stage; a processor with an N-stage pipeline can have up to N different instructions at different stages of completion and thus can issue one instruction per clock cycle (IPC = 1). These processors are known as scalar processors. The canonical example of a pipelined processor is a RISC processor, with five stages: instruction fetch (IF), instruction decode (ID), execute (EX), memory access (MEM), and register write back (WB). The Pentium 4 processor had a 35-stage pipeline.[40]

Most modern processors also have multiple execution units. They usually combine this feature with pipelining and thus can issue more than one instruction per clock cycle (IPC > 1). These processors are known as superscalar processors. Superscalar processors differ from multi-core processors in that the several execution units are not entire processors (i.e. processing units). Instructions can be grouped together only if there is no data dependency between them. Scoreboarding and the Tomasulo algorithm (which is similar to scoreboarding but makes use of register renaming) are two of the most common techniques for implementing out-of-order execution and instruction-level parallelism.
Task parallelism
[edit]Task parallelisms is the characteristic of a parallel program that "entirely different calculations can be performed on either the same or different sets of data".[41] This contrasts with data parallelism, where the same calculation is performed on the same or different sets of data. Task parallelism involves the decomposition of a task into sub-tasks and then allocating each sub-task to a processor for execution. The processors would then execute these sub-tasks concurrently and often cooperatively. Task parallelism does not usually scale with the size of a problem.[42]
Superword level parallelism
[edit]Superword level parallelism is a vectorization technique based on loop unrolling and basic block vectorization. It is distinct from loop vectorization algorithms in that it can exploit parallelism of inline code, such as manipulating coordinates, color channels or in loops unrolled by hand.[43]
Hardware
[edit]Memory and communication
[edit]Main memory in a parallel computer is either shared memory (shared between all processing elements in a single address space), or distributed memory (in which each processing element has its own local address space).[44] Distributed memory refers to the fact that the memory is logically distributed, but often implies that it is physically distributed as well. Distributed shared memory and memory virtualization combine the two approaches, where the processing element has its own local memory and access to the memory on non-local processors. Accesses to local memory are typically faster than accesses to non-local memory. On the supercomputers, distributed shared memory space can be implemented using the programming model such as PGAS. This model allows processes on one compute node to transparently access the remote memory of another compute node. All compute nodes are also connected to an external shared memory system via high-speed interconnect, such as Infiniband, this external shared memory system is known as burst buffer, which is typically built from arrays of non-volatile memory physically distributed across multiple I/O nodes.

Computer architectures in which each element of main memory can be accessed with equal latency and bandwidth are known as uniform memory access (UMA) systems. Typically, that can be achieved only by a shared memory system, in which the memory is not physically distributed. A system that does not have this property is known as a non-uniform memory access (NUMA) architecture. Distributed memory systems have non-uniform memory access.
Computer systems make use of caches—small and fast memories located close to the processor which store temporary copies of memory values (nearby in both the physical and logical sense). Parallel computer systems have difficulties with caches that may store the same value in more than one location, with the possibility of incorrect program execution. These computers require a cache coherency system, which keeps track of cached values and strategically purges them, thus ensuring correct program execution. Bus snooping is one of the most common methods for keeping track of which values are being accessed (and thus should be purged). Designing large, high-performance cache coherence systems is a very difficult problem in computer architecture. As a result, shared memory computer architectures do not scale as well as distributed memory systems do.[44]
Processor–processor and processor–memory communication can be implemented in hardware in several ways, including via shared (either multiported or multiplexed) memory, a crossbar switch, a shared bus or an interconnect network of a myriad of topologies including star, ring, tree, hypercube, fat hypercube (a hypercube with more than one processor at a node), or n-dimensional mesh.
Parallel computers based on interconnected networks need to have some kind of routing to enable the passing of messages between nodes that are not directly connected. The medium used for communication between the processors is likely to be hierarchical in large multiprocessor machines.
Classes of parallel computers
[edit]Parallel computers can be roughly classified according to the level at which the hardware supports parallelism. This classification is broadly analogous to the distance between basic computing nodes. These are not mutually exclusive; for example, clusters of symmetric multiprocessors are relatively common.
Multi-core computing
[edit]A multi-core processor is a processor that includes multiple processing units (called "cores") on the same chip. This processor differs from a superscalar processor, which includes multiple execution units and can issue multiple instructions per clock cycle from one instruction stream (thread); in contrast, a multi-core processor can issue multiple instructions per clock cycle from multiple instruction streams. IBM's Cell microprocessor, designed for use in the Sony PlayStation 3, is a prominent multi-core processor. Each core in a multi-core processor can potentially be superscalar as well—that is, on every clock cycle, each core can issue multiple instructions from one thread.
Simultaneous multithreading (of which Intel's Hyper-Threading is the best known) was an early form of pseudo-multi-coreism. A processor capable of concurrent multithreading includes multiple execution units in the same processing unit—that is it has a superscalar architecture—and can issue multiple instructions per clock cycle from multiple threads. Temporal multithreading on the other hand includes a single execution unit in the same processing unit and can issue one instruction at a time from multiple threads.
Symmetric multiprocessing
[edit]A symmetric multiprocessor (SMP) is a computer system with multiple identical processors that share memory and connect via a bus.[45] Bus contention prevents bus architectures from scaling. As a result, SMPs generally do not comprise more than 32 processors.[46] Because of the small size of the processors and the significant reduction in the requirements for bus bandwidth achieved by large caches, such symmetric multiprocessors are extremely cost-effective, provided that a sufficient amount of memory bandwidth exists.[45]
Distributed computing
[edit]A distributed computer (also known as a distributed memory multiprocessor) is a distributed memory computer system in which the processing elements are connected by a network. Distributed computers are highly scalable. The terms "concurrent computing", "parallel computing", and "distributed computing" have a lot of overlap, and no clear distinction exists between them.[47][48] The same system may be characterized both as "parallel" and "distributed"; the processors in a typical distributed system run concurrently in parallel.[49][50]
Cluster computing
[edit]
A cluster is a group of loosely coupled computers that work together closely, so that in some respects they can be regarded as a single computer.[51] Clusters are composed of multiple standalone machines connected by a network. While machines in a cluster do not have to be symmetric, load balancing is more difficult if they are not. The most common type of cluster is the Beowulf cluster, which is a cluster implemented on multiple identical commercial off-the-shelf computers connected with a TCP/IP Ethernet local area network.[52] Beowulf technology was originally developed by Thomas Sterling and Donald Becker. 87% of all Top500 supercomputers are clusters.[53] The remaining are Massively Parallel Processors, explained below.
Because grid computing systems (described below) can easily handle embarrassingly parallel problems, modern clusters are typically designed to handle more difficult problems—problems that require nodes to share intermediate results with each other more often. This requires a high bandwidth and, more importantly, a low-latency interconnection network. Many historic and current supercomputers use customized high-performance network hardware specifically designed for cluster computing, such as the Cray Gemini network.[54] As of 2014, most current supercomputers use some off-the-shelf standard network hardware, often Myrinet, InfiniBand, or Gigabit Ethernet.
Massively parallel computing
[edit]
A massively parallel processor (MPP) is a single computer with many networked processors. MPPs have many of the same characteristics as clusters, but MPPs have specialized interconnect networks (whereas clusters use commodity hardware for networking). MPPs also tend to be larger than clusters, typically having "far more" than 100 processors.[55] In an MPP, "each CPU contains its own memory and copy of the operating system and application. Each subsystem communicates with the others via a high-speed interconnect."[56]
IBM's Blue Gene/L, the fifth fastest supercomputer in the world according to the June 2009 TOP500 ranking, is an MPP.
Grid computing
[edit]Grid computing is the most distributed form of parallel computing. It makes use of computers communicating over the Internet to work on a given problem. Because of the low bandwidth and extremely high latency available on the Internet, distributed computing typically deals only with embarrassingly parallel problems.
Most grid computing applications use middleware (software that sits between the operating system and the application to manage network resources and standardize the software interface). The most common grid computing middleware is the Berkeley Open Infrastructure for Network Computing (BOINC). Often volunteer computing software makes use of "spare cycles", performing computations at times when a computer is idling.[57]
Cloud computing
[edit]The ubiquity of Internet brought the possibility of large-scale cloud computing.
Specialized parallel computers
[edit]Within parallel computing, there are specialized parallel devices that remain niche areas of interest. While not domain-specific, they tend to be applicable to only a few classes of parallel problems.
Reconfigurable computing with field-programmable gate arrays
[edit]Reconfigurable computing is the use of a field-programmable gate array (FPGA) as a co-processor to a general-purpose computer. An FPGA is, in essence, a computer chip that can rewire itself for a given task.
FPGAs can be programmed with hardware description languages such as VHDL[58] or Verilog.[59] Several vendors have created C to HDL languages that attempt to emulate the syntax and semantics of the C programming language, with which most programmers are familiar. The best known C to HDL languages are Mitrion-C, Impulse C, and Handel-C. Specific subsets of SystemC based on C++ can also be used for this purpose.
AMD's decision to open its HyperTransport technology to third-party vendors has become the enabling technology for high-performance reconfigurable computing.[60] According to Michael R. D'Amour, Chief Operating Officer of DRC Computer Corporation, "when we first walked into AMD, they called us 'the socket stealers.' Now they call us their partners."[60]
General-purpose computing on graphics processing units (GPGPU)
[edit]
General-purpose computing on graphics processing units (GPGPU) is a fairly recent trend in computer engineering research. GPUs are co-processors that have been heavily optimized for computer graphics processing.[61] Computer graphics processing is a field dominated by data parallel operations—particularly linear algebra matrix operations.
In the early days, GPGPU programs used the normal graphics APIs for executing programs. However, several new programming languages and platforms have been built to do general purpose computation on GPUs with both Nvidia and AMD releasing programming environments with CUDA and Stream SDK respectively. Other GPU programming languages include BrookGPU, PeakStream, and RapidMind. Nvidia has also released specific products for computation in their Tesla series. The technology consortium Khronos Group has released the OpenCL specification, which is a framework for writing programs that execute across platforms consisting of CPUs and GPUs. AMD, Apple, Intel, Nvidia and others are supporting OpenCL.
Application-specific integrated circuits
[edit]Several application-specific integrated circuit (ASIC) approaches have been devised for dealing with parallel applications.[62][63][64]
Because an ASIC is (by definition) specific to a given application, it can be fully optimized for that application. As a result, for a given application, an ASIC tends to outperform a general-purpose computer. However, ASICs are created by UV photolithography. This process requires a mask set, which can be extremely expensive. A mask set can cost over a million US dollars.[65] (The smaller the transistors required for the chip, the more expensive the mask will be.) Meanwhile, performance increases in general-purpose computing over time (as described by Moore's law) tend to wipe out these gains in only one or two chip generations.[60] High initial cost, and the tendency to be overtaken by Moore's-law-driven general-purpose computing, has rendered ASICs unfeasible for most parallel computing applications. However, some have been built. One example is the PFLOPS RIKEN MDGRAPE-3 machine which uses custom ASICs for molecular dynamics simulation.
Vector processors
[edit]
A vector processor is a CPU or computer system that can execute the same instruction on large sets of data. Vector processors have high-level operations that work on linear arrays of numbers or vectors. An example vector operation is A = B × C, where A, B, and C are each 64-element vectors of 64-bit floating-point numbers.[66] They are closely related to Flynn's SIMD classification.[66]
Cray computers became famous for their vector-processing computers in the 1970s and 1980s. However, vector processors—both as CPUs and as full computer systems—have generally disappeared. Modern processor instruction sets do include some vector processing instructions, such as with Freescale Semiconductor's AltiVec and Intel's Streaming SIMD Extensions (SSE).
Software
[edit]Parallel programming languages
[edit]Concurrent programming languages, libraries, APIs, and parallel programming models (such as algorithmic skeletons) have been created for programming parallel computers. These can generally be divided into classes based on the assumptions they make about the underlying memory architecture—shared memory, distributed memory, or shared distributed memory. Shared memory programming languages communicate by manipulating shared memory variables. Distributed memory uses message passing. POSIX Threads and OpenMP are two of the most widely used shared memory APIs, whereas Message Passing Interface (MPI) is the most widely used message-passing system API.[67] One concept used in programming parallel programs is the future concept, where one part of a program promises to deliver a required datum to another part of a program at some future time.
Efforts to standardize parallel programming include an open standard called OpenHMPP for hybrid multi-core parallel programming. The OpenHMPP directive-based programming model offers a syntax to efficiently offload computations on hardware accelerators and to optimize data movement to/from the hardware memory using remote procedure calls.
The rise of consumer GPUs has led to support for compute kernels, either in graphics APIs (referred to as compute shaders), in dedicated APIs (such as OpenCL), or in other language extensions.
Automatic parallelization
[edit]Automatic parallelization of a sequential program by a compiler is the "holy grail" of parallel computing, especially with the aforementioned limit of processor frequency. Despite decades of work by compiler researchers, automatic parallelization has had only limited success.[68]
Mainstream parallel programming languages remain either explicitly parallel or (at best) partially implicit, in which a programmer gives the compiler directives for parallelization. A few fully implicit parallel programming languages exist—SISAL, Parallel Haskell, SequenceL, SystemC (for FPGAs), Mitrion-C, VHDL, and Verilog.
Application checkpointing
[edit]As a computer system grows in complexity, the mean time between failures usually decreases. Application checkpointing is a technique whereby the computer system takes a "snapshot" of the application—a record of all current resource allocations and variable states, akin to a core dump—; this information can be used to restore the program if the computer should fail. Application checkpointing means that the program has to restart from only its last checkpoint rather than the beginning. While checkpointing provides benefits in a variety of situations, it is especially useful in highly parallel systems with a large number of processors used in high performance computing.[69]
Algorithmic methods
[edit]As parallel computers become larger and faster, we are now able to solve problems that had previously taken too long to run. Fields as varied as bioinformatics (for protein folding and sequence analysis) and economics have taken advantage of parallel computing. Common types of problems in parallel computing applications include:[70]
- Dense linear algebra
- Sparse linear algebra
- Spectral methods (such as Cooley–Tukey fast Fourier transform)
- N-body problems (such as Barnes–Hut simulation)
- Structured grid problems (such as Lattice Boltzmann methods)
- Unstructured grid problems (such as found in finite element analysis)
- Monte Carlo method
- Combinational logic (such as brute-force cryptographic techniques)
- Graph traversal (such as sorting algorithms)
- Dynamic programming
- Branch and bound methods
- Graphical models (such as detecting hidden Markov models and constructing Bayesian networks)
- HBJ model, a concise message-passing model[71]
- Finite-state machine simulation
Fault tolerance
[edit]Parallel computing can also be applied to the design of fault-tolerant computer systems, particularly via lockstep systems performing the same operation in parallel. This provides redundancy in case one component fails, and also allows automatic error detection and error correction if the results differ. These methods can be used to help prevent single-event upsets caused by transient errors.[72] Although additional measures may be required in embedded or specialized systems, this method can provide a cost-effective approach to achieve n-modular redundancy in commercial off-the-shelf systems.
History
[edit]
The origins of true (MIMD) parallelism go back to Luigi Federico Menabrea and his Sketch of the Analytic Engine Invented by Charles Babbage.[74][75][76]
In 1957, Compagnie des Machines Bull announced the first computer architecture specifically designed for parallelism, the Gamma 60.[77] It utilized a fork-join model and a "Program Distributor" to dispatch and collect data to and from independent processing units connected to a central memory.[78][79]
In April 1958, Stanley Gill (Ferranti) discussed parallel programming and the need for branching and waiting.[80] Also in 1958, IBM researchers John Cocke and Daniel Slotnick discussed the use of parallelism in numerical calculations for the first time.[81] Burroughs Corporation introduced the D825 in 1962, a four-processor computer that accessed up to 16 memory modules through a crossbar switch.[82] In 1967, Amdahl and Slotnick published a debate about the feasibility of parallel processing at American Federation of Information Processing Societies Conference.[81] It was during this debate that Amdahl's law was coined to define the limit of speed-up due to parallelism.
In 1969, Honeywell introduced its first Multics system, a symmetric multiprocessor system capable of running up to eight processors in parallel.[81] C.mmp, a multi-processor project at Carnegie Mellon University in the 1970s, was among the first multiprocessors with more than a few processors. The first bus-connected multiprocessor with snooping caches was the Synapse N+1 in 1984.[75]
SIMD parallel computers can be traced back to the 1970s. The motivation behind early SIMD computers was to amortize the gate delay of the processor's control unit over multiple instructions.[83] In 1964, Slotnick had proposed building a massively parallel computer for the Lawrence Livermore National Laboratory.[81] His design was funded by the US Air Force, which was the earliest SIMD parallel-computing effort, ILLIAC IV.[81] The key to its design was a fairly high parallelism, with up to 256 processors, which allowed the machine to work on large datasets in what would later be known as vector processing. However, ILLIAC IV was called "the most infamous of supercomputers", because the project was only one-fourth completed, but took 11 years and cost almost four times the original estimate.[73] When it was finally ready to run its first real application in 1976, it was outperformed by existing commercial supercomputers such as the Cray-1.
Biological brain as massively parallel computer
[edit]In the early 1970s, at the MIT Computer Science and Artificial Intelligence Laboratory, Marvin Minsky and Seymour Papert started developing the Society of Mind theory, which views the biological brain as massively parallel computer. In 1986, Minsky published The Society of Mind, which claims that "mind is formed from many little agents, each mindless by itself".[84] The theory attempts to explain how what we call intelligence could be a product of the interaction of non-intelligent parts. Minsky says that the biggest source of ideas about the theory came from his work in trying to create a machine that uses a robotic arm, a video camera, and a computer to build with children's blocks.[85]
Similar models (which also view the biological brain as a massively parallel computer, i.e., the brain is made up of a constellation of independent or semi-independent agents) were also described by:
- Thomas R. Blakeslee,[86]
- Michael S. Gazzaniga,[87][88]
- Robert E. Ornstein,[89]
- Ernest Hilgard,[90][91]
- Michio Kaku,[92]
- George Ivanovich Gurdjieff,[93]
- Neurocluster Brain Model.[94]
See also
[edit]- Computer multitasking
- Concurrency (computer science)
- Content Addressable Parallel Processor
- List of distributed computing conferences
- Loop-level parallelism
- Manchester dataflow machine
- Manycore
- Parallel programming model
- Parallelization contract
- Serializability
- Synchronous programming
- Transputer
- Vector processing
References
[edit]- ^ Gottlieb, Allan; Almasi, George S. (1989). Highly parallel computing. Redwood City, Calif.: Benjamin/Cummings. ISBN 978-0-8053-0177-9.
- ^ S.V. Adve et al. (November 2008). "Parallel Computing Research at Illinois: The UPCRC Agenda" Archived 2018-01-11 at the Wayback Machine (PDF). Parallel@Illinois, University of Illinois at Urbana-Champaign. "The main techniques for these performance benefits—increased clock frequency and smarter but increasingly complex architectures—are now hitting the so-called power wall. The computer industry has accepted that future performance increases must largely come from increasing the number of processors (or cores) on a die, rather than making a single core go faster."
- ^ Asanovic et al. Old [conventional wisdom]: Power is free, but transistors are expensive. New [conventional wisdom] is [that] power is expensive, but transistors are "free".
- ^ Asanovic, Krste et al. (December 18, 2006). "The Landscape of Parallel Computing Research: A View from Berkeley" (PDF). University of California, Berkeley. Technical Report No. UCB/EECS-2006-183. "Old [conventional wisdom]: Increasing clock frequency is the primary method of improving processor performance. New [conventional wisdom]: Increasing parallelism is the primary method of improving processor performance… Even representatives from Intel, a company generally associated with the 'higher clock-speed is better' position, warned that traditional approaches to maximizing performance through maximizing clock speed have been pushed to their limits."
- ^ Parallel and Concurrent Programming in Haskell. O'Reilly Media. 2013. ISBN 9781449335922.
- ^ Hennessy, John L.; Patterson, David A.; Larus, James R. (1999). Computer organization and design: the hardware/software interface (2. ed., 3rd print. ed.). San Francisco: Kaufmann. ISBN 978-1-55860-428-5.
- ^ a b Barney, Blaise. "Introduction to Parallel Computing". Lawrence Livermore National Laboratory. Retrieved 2007-11-09.
- ^ Thomas Rauber; Gudula Rünger (2013). Parallel Programming: for Multicore and Cluster Systems. Springer Science & Business Media. p. 1. ISBN 9783642378010.
- ^ Hennessy, John L.; Patterson, David A. (2002). Computer architecture / a quantitative approach (3rd ed.). San Francisco, Calif.: International Thomson. p. 43. ISBN 978-1-55860-724-8.
- ^ Rabaey, Jan M. (1996). Digital integrated circuits : a design perspective. Upper Saddle River, N.J.: Prentice-Hall. p. 235. ISBN 978-0-13-178609-7.
- ^ Flynn, Laurie J. (8 May 2004). "Intel Halts Development Of 2 New Microprocessors". New York Times. Retrieved 5 June 2012.
- ^ Thomas Rauber; Gudula Rünger (2013). Parallel Programming: for Multicore and Cluster Systems. Springer Science & Business Media. p. 2. ISBN 9783642378010.
- ^ Thomas Rauber; Gudula Rünger (2013). Parallel Programming: for Multicore and Cluster Systems. Springer Science & Business Media. p. 3. ISBN 9783642378010.
- ^ Bakos, Jason D. (2016-01-01), Bakos, Jason D. (ed.), "Chapter 2 - Multicore and data-level optimization: OpenMP and SIMD", Embedded Systems, Boston: Morgan Kaufmann, pp. 49–103, doi:10.1016/b978-0-12-800342-8.00002-x, ISBN 978-0-12-800342-8, retrieved 2024-11-18
- ^ The Art of Multiprocessor Programming, Revised Reprint. Morgan Kaufmann. 22 May 2012. ISBN 9780123973375.
- ^ Vajda, András (10 June 2011). Programming Many-Core Chips. Springer. ISBN 9781441997395.
- ^ Amdahl, Gene M. (1967-04-18). "Validity of the single processor approach to achieving large scale computing capabilities". Proceedings of the April 18-20, 1967, spring joint computer conference on - AFIPS '67 (Spring). New York, NY, USA: Association for Computing Machinery. pp. 483–485. doi:10.1145/1465482.1465560. ISBN 978-1-4503-7895-6.
- ^ Computer Architecture: A Quantitative Approach. Morgan Kaufmann. 2003. ISBN 978-8178672663.
- ^ Parallel Computer Architecture A Hardware/Software Approach. Elsevier Science. 1999. ISBN 9781558603431.
- ^ McCool, Michael; Reinders, James; Robison, Arch (2013). Structured Parallel Programming: Patterns for Efficient Computation. Elsevier. p. 61. ISBN 978-0-12-415993-8.
- ^ Gunther, Neil (2007). Guerrilla Capacity Planning: A Tactical Approach to Planning for Highly Scalable Applications and Services. ISBN 978-3540261384.
- ^ Bernstein, Arthur J. (1 October 1966). "Analysis of Programs for Parallel Processing". IEEE Transactions on Electronic Computers. EC-15 (5): 757–763. doi:10.1109/PGEC.1966.264565.
- ^ Roosta, Seyed H. (2000). Parallel processing and parallel algorithms : theory and computation. New York, NY [u.a.]: Springer. p. 114. ISBN 978-0-387-98716-3.
- ^ "Processes and Threads". Microsoft Developer Network. Microsoft Corp. 2018. Retrieved 2018-05-10.
- ^ Krauss, Kirk J (2018). "Thread Safety for Performance". Develop for Performance. Archived from the original on 2018-05-13. Retrieved 2018-05-10.
- ^ Tanenbaum, Andrew S. (2002-02-01). Introduction to Operating System Deadlocks. Pearson Education, Informit. Retrieved 2018-05-10.
- ^ Cecil, David (2015-11-03). "Synchronization internals – the semaphore". Embedded. AspenCore. Retrieved 2018-05-10.
- ^ Preshing, Jeff (2012-06-08). "An Introduction to Lock-Free Programming". Preshing on Programming. Retrieved 2018-05-10.
- ^ "What's the opposite of "embarrassingly parallel"?". StackOverflow. Retrieved 2018-05-10.
- ^ Schwartz, David (2011-08-15). "What is thread contention?". StackOverflow. Retrieved 2018-05-10.
- ^ Kukanov, Alexey (2008-03-04). "Why a simple test can get parallel slowdown". Retrieved 2015-02-15.
- ^ Krauss, Kirk J (2018). "Threading for Performance". Develop for Performance. Archived from the original on 2018-05-13. Retrieved 2018-05-10.
- ^ Flynn, Michael J. (September 1972). "Some Computer Organizations and Their Effectiveness" (PDF). IEEE Transactions on Computers. C-21 (9): 948–960. doi:10.1109/TC.1972.5009071.
- ^ Patterson and Hennessy, p. 748.
- ^ Silberschatz, Abraham; Galvin, Peter B.; Gagne, Greg (29 July 2008). Operating System Concepts. Wiley. ISBN 978-0470128725.
- ^ Computer Organization and Design MIPS Edition: The Hardware/Software Interface (The Morgan Kaufmann Series in Computer Architecture and Design). Morgan Kaufmann. 2013. ISBN 978-0124077263.
- ^ Parallel Programming: Techniques and Applications Using Networked Workstations and Parallel Computers. Pearson. 2005. ISBN 978-0131405639.
- ^ Singh, David Culler; J.P. (1997). Parallel computer architecture ([Nachdr.] ed.). San Francisco: Morgan Kaufmann Publ. p. 15. ISBN 978-1-55860-343-1.
{{cite book}}: CS1 maint: multiple names: authors list (link) - ^ Culler et al. p. 15.
- ^ Patt, Yale (April 2004). "The Microprocessor Ten Years From Now: What Are The Challenges, How Do We Meet Them? Archived 2008-04-14 at the Wayback Machine (wmv). Distinguished Lecturer talk at Carnegie Mellon University. Retrieved on November 7, 2007.
- ^ Culler et al. p. 124.
- ^ Culler et al. p. 125.
- ^ Samuel Larsen; Saman Amarasinghe. "Exploiting Superword Level Parallelism with Multimedia Instruction Sets" (PDF).
- ^ a b Patterson and Hennessy, p. 713.
- ^ a b Hennessy and Patterson, p. 549.
- ^ Patterson and Hennessy, p. 714.
- ^ Ghosh, Sukumar (2007). Distributed Systems – An Algorithmic Approach. Chapman & Hall/CRC. p. 10. ISBN 978-1-58488-564-1.
- ^ Keidar, Idit (2008). "Distributed computing column 32 – The year in review". ACM SIGACT News. 39 (4): 53–54. CiteSeerX 10.1.1.116.1285. doi:10.1145/1466390.1466402. S2CID 7607391. Archived from the original on 2014-01-16. Retrieved 2009-08-20.
- ^ Lynch, Nancy A. (1996), Distributed Algorithms, Morgan Kaufmann, p. 1–2, ISBN 978-1-55860-348-6
- ^ Peleg, David (2000). Distributed Computing: A Locality-Sensitive Approach. SIAM. p. 1. ISBN 978-0-89871-464-7. Archived from the original on 2009-08-06. Retrieved 2009-07-16.
- ^ What is clustering? Webopedia computer dictionary. Retrieved on November 7, 2007.
- ^ Beowulf definition. Archived 2012-10-10 at the Wayback Machine PC Magazine. Retrieved on November 7, 2007.
- ^ "List Statistics | TOP500 Supercomputer Sites". www.top500.org. Retrieved 2018-08-05.
- ^ "Interconnect" Archived 2015-01-28 at the Wayback Machine.
- ^ Hennessy and Patterson, p. 537.
- ^ MPP Definition. Archived 2013-05-11 at the Wayback Machine PC Magazine. Retrieved on November 7, 2007.
- ^ Kirkpatrick, Scott (2003). "COMPUTER SCIENCE: Rough Times Ahead". Science. 299 (5607): 668–669. doi:10.1126/science.1081623. PMID 12560537. S2CID 60622095.
- ^ Valueva, Maria; Valuev, Georgii; Semyonova, Nataliya; Lyakhov, Pavel; Chervyakov, Nikolay; Kaplun, Dmitry; Bogaevskiy, Danil (2019-06-20). "Construction of Residue Number System Using Hardware Efficient Diagonal Function". Electronics. 8 (6): 694. doi:10.3390/electronics8060694. ISSN 2079-9292.
All simulated circuits were described in very high speed integrated circuit (VHSIC) hardware description language (VHDL). Hardware modeling was performed on Xilinx FPGA Artix 7 xc7a200tfbg484-2.
- ^ Gupta, Ankit; Suneja, Kriti (May 2020). "Hardware Design of Approximate Matrix Multiplier based on FPGA in Verilog". 2020 4th International Conference on Intelligent Computing and Control Systems (ICICCS). Madurai, India: IEEE. pp. 496–498. doi:10.1109/ICICCS48265.2020.9121004. ISBN 978-1-7281-4876-2. S2CID 219990653.
- ^ a b c D'Amour, Michael R., Chief Operating Officer, DRC Computer Corporation. "Standard Reconfigurable Computing". Invited speaker at the University of Delaware, February 28, 2007.
- ^ Boggan, Sha'Kia and Daniel M. Pressel (August 2007). GPUs: An Emerging Platform for General-Purpose Computation (PDF). ARL-SR-154, U.S. Army Research Lab. Retrieved on November 7, 2007.
- ^ Maslennikov, Oleg (2002). "Systematic Generation of Executing Programs for Processor Elements in Parallel ASIC or FPGA-Based Systems and Their Transformation into VHDL-Descriptions of Processor Element Control Units". Lecture Notes in Computer Science, 2328/2002: p. 272.
- ^ Shimokawa, Y.; Fuwa, Y.; Aramaki, N. (18–21 November 1991). "A parallel ASIC VLSI neurocomputer for a large number of neurons and billion connections per second speed". [Proceedings] 1991 IEEE International Joint Conference on Neural Networks. Vol. 3. pp. 2162–2167. doi:10.1109/IJCNN.1991.170708. ISBN 978-0-7803-0227-3. S2CID 61094111.
- ^ Acken, Kevin P.; Irwin, Mary Jane; Owens, Robert M. (July 1998). "A Parallel ASIC Architecture for Efficient Fractal Image Coding". The Journal of VLSI Signal Processing. 19 (2): 97–113. Bibcode:1998JSPSy..19...97A. doi:10.1023/A:1008005616596. S2CID 2976028.
- ^ Kahng, Andrew B. (June 21, 2004) "Scoping the Problem of DFM in the Semiconductor Industry Archived 2008-01-31 at the Wayback Machine." University of California, San Diego. "Future design for manufacturing (DFM) technology must reduce design [non-recoverable expenditure] cost and directly address manufacturing [non-recoverable expenditures]—the cost of a mask set and probe card—which is well over $1 million at the 90 nm technology node and creates a significant damper on semiconductor-based innovation."
- ^ a b Patterson and Hennessy, p. 751.
- ^ The Sidney Fernbach Award given to MPI inventor Bill Gropp Archived 2011-07-25 at the Wayback Machine refers to MPI as "the dominant HPC communications interface"
- ^ Shen, John Paul; Lipasti, Mikko H. (2004). Modern processor design: fundamentals of superscalar processors (1st ed.). Dubuque, Iowa: McGraw-Hill. p. 561. ISBN 978-0-07-057064-1.
However, the holy grail of such research—automated parallelization of serial programs—has yet to materialize. While automated parallelization of certain classes of algorithms has been demonstrated, such success has largely been limited to scientific and numeric applications with predictable flow control (e.g., nested loop structures with statically determined iteration counts) and statically analyzable memory access patterns. (e.g., walks over large multidimensional arrays of float-point data).
- ^ Encyclopedia of Parallel Computing, Volume 4 by David Padua 2011 ISBN 0387097651 page 265
- ^ Asanovic, Krste, et al. (December 18, 2006). "The Landscape of Parallel Computing Research: A View from Berkeley" (PDF). University of California, Berkeley. Technical Report No. UCB/EECS-2006-183. See table on pages 17–19.
- ^ David R., Helman; David A., Bader; JaJa, Joseph (1998). "A Randomized Parallel Sorting Algorithm with an Experimental Study" (PDF). Journal of Parallel and Distributed Computing. 52: 1–23. doi:10.1006/jpdc.1998.1462. hdl:1903/835. Archived from the original (PDF) on 19 November 2012. Retrieved 26 October 2012.
- ^ Dobel, B., Hartig, H., & Engel, M. (2012) "Operating system support for redundant multithreading". Proceedings of the Tenth ACM International Conference on Embedded Software, 83–92. doi:10.1145/2380356.2380375
- ^ a b Patterson and Hennessy, pp. 749–50: "Although successful in pushing several technologies useful in later projects, the ILLIAC IV failed as a computer. Costs escalated from the $8 million estimated in 1966 to $31 million by 1972, despite the construction of only a quarter of the planned machine . It was perhaps the most infamous of supercomputers. The project started in 1965 and ran its first real application in 1976."
- ^ Menabrea, L. F. (1842). Sketch of the Analytic Engine Invented by Charles Babbage. Bibliothèque Universelle de Genève. Retrieved on November 7, 2007. quote: "when a long series of identical computations is to be performed, such as those required for the formation of numerical tables, the machine can be brought into play so as to give several results at the same time, which will greatly abridge the whole amount of the processes."
- ^ a b Patterson and Hennessy, p. 753.
- ^ R.W. Hockney, C.R. Jesshope. Parallel Computers 2: Architecture, Programming and Algorithms, Volume 2. 1988. p. 8 quote: "The earliest reference to parallelism in computer design is thought to be in General L. F. Menabrea's publication in… 1842, entitled Sketch of the Analytical Engine Invented by Charles Babbage".
- ^ Bataille, M. (1972-04-01). "Something old: the Gamma 60 the computer that was ahead of its time". ACM SIGARCH Computer Architecture News. 1 (2): 10–15. doi:10.1145/641276.641278. ISSN 0163-5964. S2CID 34642285.
- ^ "Architecture Sketch of Bull Gamma 60 -- Mark Smotherman". www.feb-patrimoine.com. Retrieved 2023-08-14.
- ^ Tumlin, Smotherman (2023-08-14). "An Evaluation of the Design of the Gamma 60". ACONIT Computer History Museum. Department of Computer Science, Clemson University. Retrieved 2023-08-14.
- ^ "Parallel Programming", S. Gill, The Computer Journal Vol. 1 #1, pp2-10, British Computer Society, April 1958.
- ^ a b c d e Wilson, Gregory V. (1994). "The History of the Development of Parallel Computing". Virginia Tech/Norfolk State University, Interactive Learning with a Digital Library in Computer Science. Retrieved 2008-01-08.
- ^ Anthes, Gry (November 19, 2001). "The Power of Parallelism". Computerworld. Archived from the original on January 31, 2008. Retrieved 2008-01-08.
- ^ Patterson and Hennessy, p. 749.
- ^ Minsky, Marvin (1986). The Society of Mind. New York: Simon & Schuster. pp. 17. ISBN 978-0-671-60740-1.
- ^ Minsky, Marvin (1986). The Society of Mind. New York: Simon & Schuster. pp. 29. ISBN 978-0-671-60740-1.
- ^ Blakeslee, Thomas (1996). Beyond the Conscious Mind. Unlocking the Secrets of the Self. Springer. pp. 6–7. ISBN 9780306452628.
- ^ Gazzaniga, Michael; LeDoux, Joseph (1978). The Integrated Mind. pp. 132–161.
- ^ Gazzaniga, Michael (1985). The Social Brain. Discovering the Networks of the Mind. Basic Books. pp. 77–79. ISBN 9780465078509.
- ^ Ornstein, Robert (1992). Evolution of Consciousness: The Origins of the Way We Think. pp. 2.
- ^ Hilgard, Ernest (1977). Divided consciousness: multiple controls in human thought and action. New York: Wiley. ISBN 978-0-471-39602-4.
- ^ Hilgard, Ernest (1986). Divided consciousness: multiple controls in human thought and action (expanded edition). New York: Wiley. ISBN 978-0-471-80572-4.
- ^ Kaku, Michio (2014). The Future of the Mind.
- ^ Ouspenskii, Pyotr (1992). "Chapter 3". In Search of the Miraculous. Fragments of an Unknown Teaching. pp. 72–83.
- ^ "Official Neurocluster Brain Model site". Retrieved July 22, 2017.
Further reading
[edit]- Rodriguez, C.; Villagra, M.; Baran, B. (29 August 2008). "Asynchronous team algorithms for Boolean Satisfiability". 2007 2nd Bio-Inspired Models of Network, Information and Computing Systems. pp. 66–69. doi:10.1109/BIMNICS.2007.4610083. S2CID 15185219.
- Sechin, A.; Parallel Computing in Photogrammetry. GIM International. #1, 2016, pp. 21–23.
External links
[edit]Parallel computing
View on GrokipediaFundamentals
Definition and Motivation
Parallel computing refers to the simultaneous use of multiple processing elements to execute computations that solve a single cohesive problem, in contrast to sequential computing, which processes tasks one at a time on a single processor. This approach leverages concurrency to divide complex problems into independent subtasks that can be handled in parallel, enabling more efficient resource utilization across hardware such as multicore CPUs or distributed systems.[15][16] The key motivations for adopting parallel computing stem from the need to accelerate solutions for large-scale computational problems, where sequential methods become prohibitively slow due to the exponential growth in data volumes and problem complexity. By distributing workloads, parallel systems can achieve significant speedups, making them essential for fields like scientific simulations and data analytics. Additionally, as Moore's Law has slowed in terms of clock frequency increases—limited by power density constraints known as the "power wall"—parallelism has become the primary avenue for performance gains beyond traditional uniprocessor scaling. Parallel computing also promotes energy efficiency by allowing computations to complete faster on specialized hardware, reducing overall power consumption compared to prolonged sequential runs, and it enables real-time applications such as autonomous systems and signal processing that demand low-latency responses.[16][17][18][19] A representative example is matrix multiplication, where a sequential algorithm exhibits O(n³) time complexity for n×n matrices, but parallel distribution across p processors can ideally yield a speedup approaching p, reducing execution time proportionally in the absence of overheads. Frameworks like Cannon's algorithm from 1969 demonstrate this by aligning data blocks on a processor mesh to minimize communication while computing products concurrently. Classifications such as Flynn's taxonomy offer a foundational lens for understanding these parallel forms without delving into specifics here.[16]Theoretical Limits
Theoretical limits in parallel computing arise from both mathematical models of speedup and fundamental physical constraints that cap the efficiency gains from additional processors. These bounds highlight why parallelization cannot indefinitely scale performance, even for highly parallelizable tasks, due to inherent serial components, resource scaling, and hardware realities. Amdahl's law provides a foundational bound on the maximum speedup achievable by parallelizing a computation. Formulated by Gene Amdahl in 1967, it assumes a fixed problem size and quantifies the impact of the serial fraction of the workload. Let denote the fraction of the computation that must be executed serially, and the number of processors. The speedup is then given by which demonstrates diminishing returns as increases: even if the parallel fraction approaches 1, the serial portion limits the overall speedup to .[13] This law underscores that parallel efficiency degrades rapidly for workloads with non-negligible serial components, such as input/output operations or initialization steps. In response to Amdahl's fixed-size assumption, Gustafson's law, proposed by John Gustafson in 1988, considers scalable problem sizes that grow with available processors, offering a more optimistic view for large-scale systems. Here, the scaled speedup for a problem adjusted to leverage processors, with serial fraction , is emphasizing that efficiency improves as parallel portions expand proportionally with resources, potentially approaching linear speedup for weakly scalable applications like simulations where problem granularity increases.[20] This formulation is particularly relevant for scientific computing, where larger datasets can exploit more processors without fixed serial bottlenecks dominating. Brent's scheduling principle establishes an upper bound on the execution time of parallel algorithms under optimal scheduling. Introduced by Richard Brent in 1974, it relates the work (total computational effort) and the critical path length (longest dependency chain) to the time on processors via indicating that even with perfect load balancing, performance is constrained by the sequential depth , preventing arbitrary speedup regardless of processor count.[21] This bound applies to expression evaluation and more general parallel computations, guiding algorithm design to minimize dependency chains. Beyond mathematical models, physical limits impose hard barriers on parallel scaling. Communication overhead, modeled in frameworks like LogP (latency, overhead, gap, processors), arises from data exchange delays between processors, which grow with system scale and can dominate computation time in distributed-memory architectures, limiting effective parallelism to applications with low inter-processor traffic.[22] The power wall, as analyzed in studies of multicore scaling, restricts active core utilization due to thermal and energy constraints; for instance, under Dennard scaling breakdown, only a fraction of transistors can operate simultaneously without exceeding power budgets, leading to "dark silicon" where much of the chip remains idle.[23] Similarly, memory bandwidth constraints form a "memory wall," where processor speeds outpace data access rates, bottlenecking parallel workloads that require frequent memory operations, as off-chip DRAM latencies and bandwidth fail to scale with core counts.[12] These physical realities collectively cap parallel efficiency, necessitating architectural innovations to approach theoretical bounds.Classifications of Parallelism
Flynn's Taxonomy
Flynn's taxonomy, introduced by Michael J. Flynn in 1966, provides a foundational classification for parallel computer architectures by distinguishing between the number of concurrent instruction streams and data streams. This two-dimensional framework—single or multiple for each stream—yields four primary categories: Single Instruction Single Data (SISD), Single Instruction Multiple Data (SIMD), Multiple Instruction Single Data (MISD), and Multiple Instruction Multiple Data (MIMD).[24] The taxonomy emphasizes architectural concurrency at the machine level, aiding in the design and evaluation of systems for parallel processing without delving into workload decomposition scales like granularity.[25] The following table summarizes the categories, their stream configurations, key characteristics, and representative examples:| Category | Instruction Streams | Data Streams | Characteristics | Examples |
|---|---|---|---|---|
| SISD | Single | Single | Sequential execution of one instruction on one data item at a time, representing conventional uniprocessor systems. | Traditional von Neumann architectures, such as early scalar processors.[24][26] |
| SIMD | Single | Multiple | A single instruction applied simultaneously to multiple data elements, enabling efficient vector or array operations. | Vector processors like the Cray-1, modern GPUs for parallel compute tasks, and Intel's Streaming SIMD Extensions (SSE) instructions for multimedia processing.[16][27][28] |
| MISD | Multiple | Single | Multiple distinct instructions operate on the same data stream, often conceptualized for specialized redundancy or pipelining. | Rarely implemented in practice; potential applications in fault-tolerant systems or systolic arrays, though no widespread commercial examples exist.[29][24] |
| MIMD | Multiple | Multiple | Independent instructions execute on separate data streams, supporting flexible, asynchronous parallelism for diverse workloads. | Multicore processors, symmetric multiprocessing systems, and distributed clusters for general-purpose computing.[24][30] |
Granularity and Types
In parallel computing, granularity refers to the size of computational tasks into which a larger problem is decomposed for concurrent execution, influencing the balance between computation and communication overheads. A finer granularity involves smaller tasks that may require frequent interactions, while coarser granularity uses larger tasks with reduced inter-task dependencies. This decomposition is crucial for optimizing performance across hardware architectures, as it determines how effectively parallelism can be exploited without excessive synchronization costs. Parallelism can be realized at multiple scales of granularity. Bit-level parallelism exploits concurrent operations on individual bits within a word, often achieved through hardware designs that increase processor word length, such as performing arithmetic on wider data paths in parallel arithmetic logic units (ALUs).[33] Instruction-level parallelism (ILP) enables the simultaneous execution of multiple instructions from a single program stream, commonly through techniques like pipelining, where instructions are broken into stages processed concurrently, or out-of-order execution, which reorders instructions to maximize overlap while respecting dependencies. Superword-level parallelism extends this by vectorizing independent operations on similar data elements, such as packing multiple scalar operations into a single vector instruction to process arrays or loops efficiently using SIMD instructions. Task-level parallelism divides a program into larger, independent subtasks assigned to separate threads or processes, allowing concurrent execution on multi-core systems or distributed nodes.[34] Parallel tasks are further classified by types based on their communication and dependency patterns, orthogonal to classifications like Flynn's taxonomy that focus on instruction and data streams. Fine-grained parallelism involves small tasks with high communication frequency, such as in pixel-by-pixel image processing where adjacent computations exchange boundary data, leading to tight coupling but potential for high throughput on tightly integrated hardware.[35] Coarse-grained parallelism, in contrast, uses larger tasks with minimal inter-task communication, exemplified by Monte Carlo simulations where independent random sampling runs on separate processors contribute to overall statistical estimates with little synchronization.[35] Embarrassing parallelism represents the extreme case of coarse granularity, where tasks are completely independent and require no communication, as in rendering independent frames or pixels in computer graphics applications. Choosing the appropriate granularity involves trade-offs between overhead from task creation, communication, and synchronization versus load balancing to ensure even distribution across processors. Finer granularity can improve load balance but increases overhead due to frequent interactions, while coarser granularity reduces overhead at the risk of uneven workloads or idle processors.[36] A key metric for assessing granularity is the communication-to-computation ratio, defined as the volume of data exchanged divided by the amount of local computation per task, which helps predict scalability; low ratios favor coarse-grained approaches on distributed systems, while higher ratios suit fine-grained execution on shared-memory platforms.Synchronization and Challenges
Dependencies and Race Conditions
In parallel computing, dependencies represent constraints that dictate the order of execution among tasks to ensure correct program behavior. Data dependencies occur when the outcome of one operation relies on the result of another, preventing concurrent execution without risking errors. These are classified into true dependencies, also known as flow or read-after-write (RAW) dependencies, where a task reads a value produced by a prior task; anti-dependencies, or write-after-read (WAR), where a task writes to a location that another task will later read; and output dependencies, or write-after-write (WAW), where multiple tasks attempt to write to the same location.[37][38] True dependencies preserve data flow and cannot be eliminated without altering semantics, while anti- and output dependencies often arise from naming conflicts and can sometimes be resolved through techniques like register renaming in hardware or variable renaming in software.[39] Control dependencies arise from branching structures, such as conditional statements or loops, where the execution path of subsequent tasks depends on the outcome of a control decision. These dependencies enforce sequential ordering to maintain logical correctness, as parallelizing across branches may lead to executing code paths that should be skipped.[40] Input and output dependencies further complicate parallelism by involving shared resources at the boundaries of tasks; input dependencies occur when multiple tasks read from the same initial data source, potentially requiring synchronization to avoid stale reads, while output dependencies manifest when tasks produce results that aggregate into a common destination, such as in reduction operations.[41] Race conditions emerge from unhandled dependencies, particularly data dependencies, resulting in non-deterministic behavior when multiple threads or processes concurrently access and modify shared resources without proper coordination. A classic example is the lost update problem in a shared counter, where two threads read the initial value simultaneously, increment it independently, and write back, causing one update to overwrite the other and yielding an incorrect final count.[42][43] This unpredictability stems from the timing of interleaving executions, which varies across runs due to scheduling nondeterminism. Detection of race conditions typically employs static analysis, which examines code without execution to identify potential conflicts through flow-sensitive interprocedural checks, or dynamic tracing, which monitors runtime accesses to shared memory and flags concurrent unsynchronized operations. Tools like RacerX exemplify static approaches by modeling pointer flows to pinpoint races conservatively, while dynamic methods, such as those in RaceTrack, record execution traces and detect suspicious access patterns with higher precision but at runtime overhead.[44][45][46] The impacts of undetected race conditions include non-reproducibility, where bugs manifest inconsistently across executions, complicating debugging, and subtle program errors that corrupt data integrity or produce incorrect outputs, undermining the reliability of parallel applications.[47][48] Such issues can propagate through computations, leading to cascading failures in large-scale systems.Mutual Exclusion and Synchronization
Mutual exclusion ensures that only one process or thread accesses a shared critical section at a time, preventing race conditions where concurrent modifications could lead to inconsistent states. Early software-based solutions for two processes include Dekker's algorithm, which uses shared flags and a turn variable to coordinate access without hardware support, achieving mutual exclusion through busy-waiting loops that check the other process's intent.[49] This approach, attributed to T.J. Dekker and formalized by E.W. Dijkstra in 1965 notes, was the first correct software solution relying solely on load and store instructions.[49] Peterson's algorithm, developed for two processes in 1981, improves on Dekker's by introducing a flag array and turn variable, allowing one process to yield priority to the other, thus ensuring progress and bounded waiting while maintaining mutual exclusion via atomic reads and writes.[50] For scalability to multiple processes, these algorithms form the basis for more general constructions, though they rely on assumptions of atomic memory operations. Hardware support simplifies mutual exclusion through atomic instructions like test-and-set, which reads a memory location and sets it to a non-zero value in a single indivisible operation, enabling simple spinlock implementations where processes repeatedly test until the lock is acquired. Synchronization primitives extend mutual exclusion to coordinate broader interactions among parallel processes. Semaphores, introduced by E.W. Dijkstra in 1968, are integer variables with atomic P (wait) and V (signal) operations: P decrements the value if positive or blocks otherwise, while V increments and wakes a waiting process, supporting both binary semaphores for locks and counting semaphores for resource pools. Barriers synchronize a group of processes by blocking each until all reach the point, ensuring collective progress; a dissemination-style barrier, for example, uses logarithmic steps where processes pairwise notify others in a tournament pattern. Monitors, proposed by C.A.R. Hoare in 1974, encapsulate shared data with procedures that implicitly enforce mutual exclusion via a single entry lock, simplifying programming by serializing access. Within monitors, condition variables enable signaling: a process waits on a condition if a predicate is false, and another signals via notify or broadcast to resume waiters, decoupling exclusion from waiting. These primitives address issues like race conditions by providing structured mechanisms for safe concurrency. In shared-memory models, synchronization relies on these primitives to manage access to common address spaces, as in multi-core systems using locks or semaphores. In contrast, message-passing models, as in distributed systems, achieve synchronization through explicit sends and receives that coordinate via point-to-point or collective operations, avoiding shared state but requiring agreement protocols for barriers or exclusions. A classic example is the producer-consumer problem, where producers add items to a bounded buffer and consumers remove them without overflow or underflow. Semaphores resolve this: a mutex semaphore ensures exclusive buffer access, an empty semaphore (initialized to buffer size) blocks consumers if empty, and a full semaphore (initialized to zero) blocks producers if full; producers signal empty after adding, and consumers signal full after removing.[semaphore](/page/Semaphore) mutex = 1;
[semaphore](/page/Semaphore) empty = N; // buffer size
[semaphore](/page/Semaphore) full = 0;
producer() {
P(empty);
P(mutex);
// add item to buffer
V(mutex);
V(full);
}
consumer() {
P(full);
P(mutex);
// remove item from buffer
V(mutex);
V(empty);
}
[semaphore](/page/Semaphore) mutex = 1;
[semaphore](/page/Semaphore) empty = N; // buffer size
[semaphore](/page/Semaphore) full = 0;
producer() {
P(empty);
P(mutex);
// add item to buffer
V(mutex);
V(full);
}
consumer() {
P(full);
P(mutex);
// remove item from buffer
V(mutex);
V(empty);
}
Parallel Slowdown and Limitations
Parallel slowdown refers to the phenomenon where adding more processors to a parallel system fails to yield proportional performance improvements, often resulting in degradation compared to ideal expectations. This occurs primarily due to Amdahl's law, which highlights that the speedup is limited by the fraction of the program that remains inherently serial, as even a small sequential portion bottlenecks the entire computation. For instance, if 5% of a program's execution is serial, the maximum theoretical speedup with infinite processors is only 20-fold, regardless of parallelizable parts. In practice, serial bottlenecks manifest in tasks like input/output operations or initialization that cannot be distributed, preventing full utilization of hardware resources.[13] Additional causes of parallel slowdown include excessive synchronization overhead and load imbalance. Synchronization overhead arises from mechanisms like barriers or locks that force processors to wait for each other, leading to idle time; studies on shared-memory multiprocessors show that serialization at critical sections and uneven workload distribution can account for a significant portion of this overhead, sometimes dominating overall performance losses. Load imbalance happens when tasks are unevenly distributed across processors, causing some to finish early while others remain busy, which is particularly pronounced in irregular data structures or adaptive algorithms. These factors compound the serial limitations, reducing actual speedup below theoretical bounds outlined in models like Amdahl's law. Parallel systems also introduce broader disadvantages, such as increased complexity in debugging and higher power consumption. Debugging parallel programs is notoriously challenging due to non-deterministic execution, race conditions, and the need to trace interactions across multiple threads, which amplifies the effort compared to sequential code; traditional tools often fail to capture non-repeatable behaviors, exacerbating development time. Power consumption rises with the number of active processors and interconnects, as each additional core draws more energy without linear performance gains—experiments indicate that power usage grows non-linearly, sometimes exceeding twice the baseline for inefficient parallelization. Scalability is further limited by communication latency, where data exchange between processors introduces delays that grow with system size, hindering performance on large-scale machines regardless of computation volume.[51] Other limitations stem from inherently non-parallelizable problems and the overhead costs in small-scale applications. Many tasks, such as sequential decision-making algorithms or problems with strong data dependencies (e.g., certain graph traversals), resist effective parallelization because each step relies on prior results, rendering distribution inefficient or impossible. In small-scale scenarios, the setup costs for parallelism— including thread management and communication—often outweigh benefits, making sequential execution more practical; this is evident when problem sizes do not justify the coordination overhead. To quantify these losses, parallel efficiency is commonly measured as the ratio of achieved speedup to the number of processors (p), where efficiency E = S_p / p and S_p is the speedup; values below 1 indicate sub-linear scaling, with typical efficiencies dropping to 50% or less in real systems due to the aforementioned issues.[13][52] These practical drawbacks contrast with idealized theoretical limits, where bounds like Amdahl's assume perfect parallelization of eligible portions but are rarely met in implementation.Hardware Architectures
Memory and Communication
In parallel computing systems, memory models define how processors access shared data, influencing performance and scalability. Uniform Memory Access (UMA) architectures provide all processors with equal and direct access to a common physical memory pool, typically through a shared bus or crossbar interconnect, ensuring consistent latency for memory operations across the system.[16] This model is common in symmetric multiprocessing (SMP) systems with a small number of processors, as it simplifies hardware design but can become a bottleneck under high contention due to the centralized memory controller. In contrast, Non-Uniform Memory Access (NUMA) architectures distribute memory modules locally to groups of processors, allowing faster access to local memory while remote access incurs higher latency, often 2-5 times greater depending on the interconnect distance.[53] NUMA scales better for larger systems by reducing contention on a single memory controller, though it requires software optimizations like affinity scheduling to minimize remote accesses. Cache coherence protocols maintain consistency among multiple local caches in shared-memory systems, preventing processors from operating on stale data. The MESI (Modified, Exclusive, Shared, Invalid) protocol, a widely adopted invalidate-based scheme, tracks the state of each cache line to ensure that writes are propagated or invalidated appropriately across caches. In MESI, a cache line in the Modified state holds the only valid copy after a write; transitioning to Shared allows multiple readers; Exclusive permits a sole read before potential modification; and Invalid marks unusable lines that must be fetched anew. This protocol reduces overhead in bus-based systems by minimizing bus traffic for reads while ensuring coherence through snooping, though it can introduce delays during state transitions in multi-level cache hierarchies. Communication in parallel systems varies by architecture, with shared-memory models relying on implicit data exchange via a common address space and distributed-memory models using explicit message passing. In bus-based shared-memory systems, processors communicate through a shared interconnect like a bus, where data is accessed directly but contention limits scalability to tens of processors.[16] Distributed-memory systems, such as clusters, employ message-passing interfaces like MPI (Message Passing Interface), where processes on separate nodes exchange data via explicit sends and receives, supporting scalability to thousands of nodes through networks like InfiniBand.[54] These approaches trade off bandwidth and latency: shared-memory offers low-latency access (microseconds) but saturates quickly due to bus bandwidth limits (e.g., 10-50 GB/s), while distributed-memory provides higher aggregate bandwidth (hundreds of GB/s across nodes) at higher per-message latency (tens of microseconds), making it suitable for loosely coupled computations.[55] Key issues in these models include memory consistency and false sharing, which can degrade performance if unaddressed. Sequential consistency requires that all memory operations appear to execute in a total order consistent with each processor's program order, as if on a uniprocessor, ensuring intuitive behavior but imposing strict hardware constraints that limit optimizations like out-of-order execution.[56] Relaxed consistency models, such as release consistency, weaken these ordering rules—allowing reads to bypass writes under certain barriers—to improve performance by enabling compiler and hardware reordering, though programmers must use synchronization primitives to enforce necessary orders. False sharing occurs when multiple processors modify distinct variables mapped to the same cache line (typically 64 bytes), triggering unnecessary coherence traffic and invalidations, which can reduce throughput by factors of 2-10 in multithreaded workloads.[57] Performance implications often stem from bandwidth bottlenecks during scaling, where increasing processor count amplifies memory demands without proportional interconnect improvements. In shared-memory systems, bus saturation leads to queuing delays, capping effective speedup at 4-8 processors for memory-intensive applications, as aggregate bandwidth fails to match parallel access rates.[58] NUMA and distributed systems mitigate this through locality-aware allocation and partitioning, but remote accesses can still impose latency walls, emphasizing the need for algorithms that minimize communication volume to achieve linear scaling.Multi-Core and Symmetric Multiprocessing
Multi-core processors integrate multiple independent processing units, known as cores, onto a single integrated circuit die, enabling simultaneous execution of multiple threads or tasks to improve overall system performance. This design emerged as a response to the limitations of increasing clock speeds in single-core processors, driven by power and thermal constraints, with early pioneering work on general-purpose chip multiprocessors conducted by Kunle Olukotun and his team at Stanford in the 1990s.[59][60] In such architectures, each core typically includes its own private L1 cache for fast access to instructions and data, while higher-level caches like L2 and L3 may be shared among cores to facilitate efficient data exchange.[61] A key feature in many multi-core designs, such as those in Intel's Core i7 processors, is hyper-threading technology, which allows each physical core to present as two logical cores to the operating system by duplicating certain architectural states and sharing execution resources. This enables better utilization of core resources during thread switches, potentially improving throughput by up to 30% in thread-heavy workloads without requiring additional physical silicon. For instance, the 14th-generation Intel Core i7 processors feature up to 20 cores (8 performance cores and 12 efficient cores) with hyper-threading on the performance cores, supporting up to 28 threads in total.[62] Symmetric multiprocessing (SMP) extends the multi-core concept to systems where multiple identical processors share a common memory space and interconnect, treating all processors as equals with uniform access to resources. In SMP configurations, the operating system schedules tasks across processors transparently, relying on hardware mechanisms like cache coherence protocols to maintain data consistency. Scalability in SMP systems typically reaches dozens of cores, limited by factors such as memory bandwidth and coherence overhead, but it provides a tightly coupled environment suitable for workloads like databases and scientific simulations.[63] Early examples of SMP include AMD's Opteron processors, introduced in 2003, which supported up to eight sockets in a shared-memory configuration using the HyperTransport interconnect for low-latency inter-processor communication.[64] Modern implementations push boundaries further; for example, AMD's EPYC processors in the 9005 series offer up to 192 cores per socket in SMP setups, while Intel's Xeon 6 series provides up to 128 performance cores (Granite Rapids) or 144 efficiency cores (Sierra Forest) per socket, enabling up to 256 or 288 cores across dual sockets, respectively, often integrated with accelerators like GPUs via on-chip links for hybrid computing.[65][66][67] One primary advantage of multi-core and SMP architectures is the low-latency communication enabled by on-chip shared caches, where data transfer between cores occurs in tens of cycles compared to hundreds or thousands in distributed systems, reducing overhead for fine-grained parallelism.[68] This shared cache hierarchy minimizes the need for explicit data movement, enhancing efficiency in cache-coherent environments that align with uniform memory access models.[69]Distributed and Specialized Systems
Distributed computing encompasses systems where multiple independent machines collaborate over a network to perform parallel tasks, typically using a message-passing paradigm to exchange data and coordinate operations. This approach allows for scalability beyond single-system limits by leveraging commodity hardware in clusters or grids, where each node operates autonomously but communicates explicitly to achieve collective computation. Unlike tightly coupled shared-memory architectures, distributed systems prioritize fault tolerance and resource pooling across geographically dispersed resources.[70] Beowulf clusters exemplify early distributed systems, aggregating off-the-shelf personal computers into a high-performance parallel environment through Ethernet interconnects and message-passing interfaces, enabling cost-effective scaling for scientific simulations without specialized hardware. In modern cloud environments, services like AWS ParallelCluster facilitate on-demand deployment of such clusters, allowing users to provision virtual machines for high-performance computing (HPC) workloads with automatic scaling and integration of parallel job schedulers. These systems support workloads ranging from weather modeling to big data analytics by distributing computations across hundreds or thousands of nodes.[71][72] Specialized hardware extends parallelism through domain-specific designs that optimize for particular computation patterns. Graphics processing units (GPUs) excel in single instruction, multiple data (SIMD) operations, where thousands of cores execute identical instructions on arrays of data simultaneously; NVIDIA's CUDA programming model enables developers to harness this for general-purpose computing, such as matrix multiplications in simulations. Field-programmable gate arrays (FPGAs) offer reconfigurable parallelism, allowing hardware logic to be customized at the gate level for irregular or dataflow-oriented tasks, providing flexibility for accelerating algorithms like signal processing that benefit from tailored pipelines. Tensor processing units (TPUs), developed by Google, are application-specific integrated circuits (ASICs) optimized for AI workloads, featuring systolic arrays for efficient parallel tensor operations in neural network training and inference.[73][74][75] Prominent examples include the Frontier supercomputer, deployed in 2022 at Oak Ridge National Laboratory, which, as of June 2025, achieved 1.353 exaFLOPS on the HPL benchmark using its 9,472 nodes equipped with AMD processors and GPUs interconnected via high-speed fabrics for distributed simulations in climate and materials science.[76][77] The IBM Blue Gene series, spanning models from Blue Gene/L to Blue Gene/Q, demonstrated massive scalability in the 2000s, with Blue Gene/L sustaining linear performance up to 131,072 nodes for applications like molecular dynamics, emphasizing low-power, distributed-memory designs. These systems typically align with the multiple instruction, multiple data (MIMD) classification in Flynn's taxonomy due to their independent processing and data streams across nodes.[78] While distributed and specialized systems can scale to millions of cores— as seen in exascale machines— inter-node communication via networks introduces higher latency than intra-node shared memory, potentially bottlenecking applications sensitive to data synchronization; optimizations like hierarchical interconnects mitigate this by reducing average message transit times. This trade-off underscores their suitability for embarrassingly parallel or loosely coupled tasks, where overall throughput gains outweigh communication overheads. As of 2025, hybrid CPU-GPU integrations continue to advance, with systems like Frontier incorporating AI accelerators for enhanced parallel workloads.[70]Software Approaches
Parallel Programming Languages
Parallel programming languages provide explicit constructs for expressing concurrency and coordination in software, enabling developers to leverage multiple processing units effectively. These languages extend traditional sequential programming models by incorporating features such as thread creation, message passing, and data distribution, which are essential for managing parallelism on shared-memory and distributed systems.[18] Early developments in the 1990s focused on message-passing paradigms to address distributed computing challenges, with the Message Passing Interface (MPI) standard emerging as a foundational specification for portable parallel programs across heterogeneous clusters.[79] Thread-based models, exemplified by POSIX threads (pthreads) in C and C++, allow explicit creation and management of lightweight processes that share memory, facilitating fine-grained parallelism within a single node. Pthreads support operations like mutexes for synchronization and condition variables for coordination, making them suitable for irregular workloads where threads execute concurrently and access shared data. In contrast, task-based models, such as Intel's oneAPI Threading Building Blocks (oneTBB), abstract parallelism through dynamic task graphs, where tasks represent units of work that the runtime scheduler distributes across cores for load balancing and scalability.[80] Data-parallel models, like those in NVIDIA's CUDA, enable massive concurrency by executing identical kernels across thousands of GPU threads, ideal for SIMD-style computations on arrays or matrices.[73] Fortran's coarray extensions, introduced in the 2008 standard, provide a partitioned global address space (PGAS) model where arrays are distributed across images (processes) with simple syntax for remote data access, reducing the verbosity of explicit messaging in scientific simulations. C++ integrates parallelism via OpenMP directives for compiler-guided loop parallelization and thread spawning, supporting both shared-memory and accelerator offloading while maintaining portability across multi-core architectures.[81] For implicit parallelism, Haskell employs strategies and monads to automatically parallelize pure functional computations, such as data-parallel operations on aggregate structures, without requiring explicit thread management.[82] Chapel, developed for high-productivity parallel computing, combines imperative and functional elements with built-in support for domains, locales, and reduction operations, allowing scalable expression of distributed tasks on large-scale systems.[83] Modern languages like Julia incorporate multi-threading and distributed computing primitives, including macros for parallel loops (@threads) and remote function calls (remotecall), enabling seamless scaling from single-node multi-core to cluster environments.[84] Key features across these languages include fork-join parallelism, where a parent task spawns subtasks that execute asynchronously before synchronizing at join points; async tasks for non-blocking execution; and atomic operations to ensure thread-safe updates to shared variables without locks.[85] These constructs, evolving from MPI's point-to-point communications in the 1990s to today's integrated models, prioritize developer productivity while mitigating synchronization overheads in diverse hardware ecosystems.[79]Automatic Parallelization and Checkpointing
Automatic parallelization refers to compiler techniques that automatically detect and exploit parallelism in sequential code without requiring explicit programmer intervention, primarily through analysis and transformation of loops. A core method involves data dependence analysis, which identifies whether iterations of a loop can execute independently, enabling transformations such as converting sequential do-loops into parallel do-loops where no inter-iteration dependencies exist. For instance, a DOALL loop, characterized by the absence of cross-iteration data dependencies, can be fully parallelized by distributing iterations across multiple threads or processors, as the compiler confirms that statements within the loop do not need sequential ordering beyond barrier synchronization at the loop boundaries.[86][87] Compilers like the Intel C++ and Fortran compilers (formerly ICC, now part of Intel oneAPI) implement these techniques using flags such as -parallel (or /Qparallel on Windows), which perform dependence analysis on loop nests starting from the outermost level and apply optimizations like loop distribution and fusion when profitable. This process often includes vectorization alongside parallelization to leverage SIMD instructions, but success depends on accurate dependence detection. Tools such as these have demonstrated speedups on benchmarks like SPEC FP, though gains vary by code structure. Recent advances as of 2024 include AI-driven tools like OMPar for inserting OpenMP pragmas and new thread-level speculative models, enhancing applicability to more complex real-world applications.[88][89][90][91] Despite these advances, automatic parallelization faces significant limitations, particularly in alias analysis, where the compiler struggles to disambiguate pointers that may reference the same memory location, leading to conservative assumptions that prevent parallelization even when safe. Profitability heuristics further constrain applicability, as compilers weigh the overhead of thread creation, synchronization, and load imbalance against potential gains, often deeming transformations unprofitable for irregular or small loops. These challenges result in parallelization rates below 50% for many real-world applications, highlighting the need for hybrid approaches combining static analysis with runtime speculation.[92][93][94] Checkpointing in parallel computing involves periodically saving the state of a running program to enable restart after failures, such as hardware faults or power outages, which is crucial for long-running jobs on large-scale systems. Transparent checkpointing operates at the system or user level without modifying application code, capturing process states, memory, and open files automatically; DMTCP (Distributed MultiThreaded CheckPointing) exemplifies this by providing transparent support for distributed and multithreaded applications across Linux clusters, using ptrace to intercept system calls and coordinate checkpoints via a coordinator process. In contrast, application-level checkpointing requires explicit code changes to save and restore domain-specific states, such as variables or data structures, offering finer control but increasing development effort.[95][96][97] For distributed parallel environments, checkpointing integrates with message-passing interfaces like MPI to ensure coordinated state saving across nodes, often using coordinated protocols where all processes synchronize before writing checkpoints to shared or local storage. This integration handles challenges like in-flight messages by quiescing communication, but introduces significant I/O overhead from serializing large memory images—up to gigabytes per process—which can substantially increase runtime depending on frequency and system configuration. Tools like Open MPI support built-in checkpointing extensions, such as CRS (Checkpoint/Restart Service), to facilitate fault tolerance in MPI jobs while minimizing downtime.[98][99][100]Parallel Algorithms
Algorithmic Methods
Algorithmic methods in parallel computing encompass strategies for structuring computations to leverage multiple processors simultaneously, aiming to minimize execution time while maintaining efficiency. These methods emphasize dividing problems into concurrent tasks, managing dependencies, and optimizing resource utilization. Central to this is the identification of inherent parallelism in algorithms, often through decomposition techniques that balance computational load and communication overhead. Seminal approaches include divide-and-conquer paradigms, which recursively partition problems into independent subproblems solvable in parallel.[101] A prominent example is the parallel merge sort algorithm, which divides an array into two halves, recursively sorts each in parallel, and then merges the sorted halves using a parallel merging step. This achieves O(log n) parallel time with O(n/log n) processors on a PRAM model, matching the sequential time bound while providing linear speedup.[102] Another key method is the prefix sum (scan) operation, which computes cumulative results over an array, such as partial sums, and serves as a primitive for more complex parallel algorithms like sorting and graph traversal. Blelloch's work-efficient parallel scan algorithm on an EREW PRAM uses an up-sweep reduction followed by a down-sweep distribution, running in O(log n) time with n processors and total work O(n).[103] The map-reduce paradigm further exemplifies algorithmic methods for large-scale data processing, where input data is mapped into intermediate key-value pairs processed independently across processors, followed by a reduction phase that aggregates results by key. This approach enables fault-tolerant parallelism on distributed systems, as demonstrated in its original implementation handling terabytes of data with automatic task distribution.[104] Parallelization techniques commonly employed include data decomposition, which partitions input data across processors so each performs identical computations on its subset, ideal for regular problems like array operations. For instance, in numerical simulations, arrays are divided into blocks assigned to processors, with communication only at boundaries. Pipeline parallelism structures algorithms as a sequence of stages where outputs from one stage feed into the next, allowing overlapping execution to hide latency, as in pipelined data-parallel algorithms that stream data through processing phases on distributed-memory systems.[105] Hybrid approaches combine these, such as using data decomposition within pipeline stages, to exploit both data and task parallelism for irregular workloads. Granularity in decomposition guides the choice of partition size to optimize load balance and minimize overhead.[101] Complexity analysis of parallel algorithms relies on models like the PRAM, introduced as an abstraction of synchronized processors with shared memory access in constant time, enabling theoretical bounds independent of hardware details. In PRAM, algorithms are evaluated by time steps T and processors P, with efficiency measured by speedup S = T_1 / T, where T_1 is sequential time. The work-depth framework complements this by separating total computational work T_1 (sum of operations) from parallel time T_p (length of critical path or depth), allowing analysis of schedulability; an algorithm is work-efficient if T_1 = O(sequential work) and T_p = O(log n) for balanced trees.[106][101] Illustrative examples include the parallel fast Fourier transform (FFT), which applies divide-and-conquer to the Cooley-Tukey decomposition, computing the discrete Fourier transform by recursively factoring into smaller DFTs executed in parallel across dimensions. This yields O(log n) depth on O(n) processors for n-point FFT, with applications in signal processing. Matrix multiplication via block distribution, as in Cannon's algorithm, decomposes matrices into blocks assigned to a processor grid, where initial alignment shifts blocks followed by local multiplications and reductions, achieving O(n^3 / P) computational time with O(n^2 / sqrt(P)) communication cost on P processors arranged in a 2D grid for n x n matrices and optimal communication on mesh topologies.[107][108]| Technique | Key Example | Parallel Time (T_p) | Processors (P) | Model |
|---|---|---|---|---|
| Divide-and-Conquer | Parallel Merge Sort | O(log n) | O(n / log n) | PRAM |
| Prefix Sum | Blelloch Scan | O(log n) | n | EREW PRAM |
| Map-Reduce | Data Processing | O(input size / P) | Scalable to thousands | Distributed |
| Data Decomposition | Block Matrix Ops | O(n^3 / P) | Up to n^2 | Mesh/Grid |
| Pipeline | Streaming Stages | O(stages + data / P) | Number of stages | Distributed-Memory |