Parallel Computing
Parallel Computing
Lecture 1
Why parallel? 1
Why we need ever-increasing performance? 1
Why we’re building ever-increasing performance? 1
The solution of parallel 1
Why we need to write parallel programs 1
Approaches to the serial problem 1
More problems + Example 1
Analysis of example #1 and #2 2
Better parallel algorithm 2
Multiple cores forming a global sum 2
How do we write parallel programs? Task Parallelism and Data Parallelism 2
Division of work: Task Parallelism and Data Parallelism 3
Coordination 3
Types of parallel systems: Shared-memory and Distrubuted Memory 3
Terminology of Concurrent, Parallel and Distributed computing 2
Concluding remarks of lecture 1 3
Lecture 2 & 3
POSIX®Threads 26
Caveat 26
Hello World 1, 2 and 3 26
Compiling a Pthread program 26
Running a Pthread program 26
Global variables 26
Starting the Threads 26
pthread_t objects 26
A closer look 1 and 2 27
Function started by pthread_create 27
Running the Threads 27
Stopping the Threads 27
Serial pseudo-code 27
Using 3 Pthreads 27
Pthreads matrix-vector multiplication 27
Estimating π and a thread function for computing π 28
Busy-Waiting 28
Possible race condition 28
Pthreads global sum with busy-waiting 28
Global sum function with critical section after loop 28
Mutexes 29
Global sum function that uses a mutex 29
Issues 29
Problems with a mutex solution 30
A first attempt at sending messages using pthreads 30
Syntax of the various Semaphore functions 30
Barriers + (Using barriers to time the slowest threads and for debugging) 30
Busy-Waiting and a Mutex 30
Implementing a barrier with Semaphores 30
Condition Variables 31
Implementing a barrier with conditional variables 31
Linked Lists (+Linked Lists Membership) 31
Inserting a new node into a list 31
Deleting a node from a linked list 31
A Multi-Threaded Linked List 32
Simultaneous access by two threads 32
Solution #1 and #2 and their issues 32
Implementation of Member with one mutex per list node 32
Pthreads Read-Write Locks 32
Protecting our Linked List functions 33
Linked List Perfomance 33
Caches, Cache-Coherence, and False Sharing 33
Pthreads matrix-vector multiplication 33
Thread-Safety (+Example) 33
Simple Approach 34
The strtok function 34
Multi-threaded tokenizer 34
Running with one thread 34
Running with two threads 34
Other unsafe C library functions 34
“re-entrant” (thread safe) functions 35
Concluding remarks about lecture 5&6 35
Lecture 7 & 8
OpenMP 35
Pragmas 35
OpenMP pragmas 36
A process forking and joining two threads 36
clause 36
Of note… 36
Some terminology 36
In case the compiler doesn’t support OpenMD 36
The Trapezoid Rule in general 36-38
Scope and Scope in OpenMD 37
The Reduction Clause 37
Reduction Operators 38
Parallel for 38
Legal forms for parallelizable for statements 38
Caveats 38
Data dependencies 39
Estimating π with OpenMD solution 39
The default clause 39
Bubble Sort 39
Serial Odd-Even Transposition Sort 39
First OpenMD Odd-Even Sort 39
Second OpenMD Odd-Even Sort 40
Scheduling loops and their Results 40
The Schedule Clause (Default schedule, Cyclic schedule) 40
Schedule(type, chunksize, static, dynamic or guided, auto, runtime) 40
The Static Schedule Type 41
The Dynamic Schedule Type 41
The Guided Schedule Type 41
The Runtime Schedule Type 41
Queues 41
Message-Passing 41
Sending Messages 41
Receiving Messages 41
Termination Detection 42
Startup 42
The Atomic Directive 42
Critical Sections 42
Locks (+Using Locks in the Message-Passing Programs) 42
Some Caveats 43
Matrix-vector multiplication 43
Thread-Safety 43
Concluding Remarks about Lecture 7 & 8 43
Lecture 9 & 10
Why Parallel?
Instead of designing and building faster microprocessors, we put multiple processors on a single integrated circuit.
Computational power is increasing, but so are our computation problems and needs.
Problems we never dreamed of have been solved because of past increases, such as decoding the human
genome.
More complex problems are still waiting to be solved.
More problems
Some coding constructs can be recognized by an automatic program generator, and converted to a parallel
construct.
However, it’s likely that the result will be a very inefficient program.
Sometimes the best parallel solution is tostep back and devise an entirely new algorithm.
Example
After each core completes execution of the code, is a private variable my_sum contains the sum of the values
computed by its calls to Compute_next_value.
Once all the cores are done computing their private my_sum, they form a global sum by sending results to a
designated “master” core which adds the final result.
2
Example #2
Analysis
In the first example, the master core performs 7 receives and 7 additions.
In the second example, the master core performs 3 receives and 3 additions.
The improvement is more than a factor of 2!
The difference is more dramatic with a larger number of cores.
If we have 1000 cores:
o The first example would require the master to perform 999 receives and 999 additions.
o The second example would only require 10 receives and 10 additions.
That’s an improvement of almost a factor of 100!
Task parallelism
o Partition various tasks carried out solving the problem among the cores.
Data parallelism
o Partition the data used in solving the problem among the cores.
o Each core carries out similar operations on it’s part of the data.
3
Coordination
Shared-memory
o The cores can share access to the computer’s
memory.
o Coordinate the cores by having them examine and
update shared memory locations.
Distributed-memory
o Each core has its own, private memory.
o The cores must communicate explicitly by sending
messages across a network
Terminology
Concurrent computing – a program is one in which multiple tasks can be in progress at any instant.
Parallel computing – a program is one in which multiple tasks cooperate closely to solve a problem
Distributed computing – a program may need to cooperate with other programs to solve a problem
Concluding Remarks
Main Memory:
This is a collection of locations, each of which is capable of storing both instructions and data.
Every location consists of an address, which is used to access the location, and the contents of the location.
Bus – wires and hardware that connects the CPU and memory
Multitasking
Gives the illusion that a single processor system is running multiple programs simultaneously.
Each process takes turns running. (time slice)
After its time is up, it waits until it has a turn again. (blocks)
Threading
Basics of caching
A collection of memory locations that can be accessed in less time than some other memory locations.
A CPU cache is typically located on the same chip, or one that can be accessed much faster than ordinary
memory
Principle of locality
When a CPU writes data to cache, the value in cache may be inconsistent with the value in main memory.
Write-through caches handle this by updating the data in main memory at the time it is written to cache.
Write-back caches mark data in the cache as dirty. When the cache line is replaced by a new cache line from
memory, the dirty line is written to memory.
Cache mappings
Virtual memory
If we run a very large program or a program that accesses very large data sets, all of the instructions and data
may not fit into main memory.
Virtual memory functions as a cache for secondary storage.
It exploits the principle of spatial and temporal locality.
It only keeps the active parts of running programs in main memory.
Swap space - those parts that are idle are kept in a block of secondary storage.
Pages – blocks of data and instructions.
o Usually these are relatively large.
o Most systems have a fixed page size that currently ranges from 4 to 16 kilobytes.
When a program is compiled its pages are assigned virtual page numbers.
When the program is run, a table is created that maps the virtual page numbers to physical addresses.
A page table is used to translate the virtual address into a physical address.
Page Table
Virtual Address divided into Virtual Page Number and Byte Offset
Using a page table has the potential to significantly increase each program’s overall run-time.
A special address translation cache in the processor.
It caches a small number of entries (typically 16–512) from the page table in very fast memory.
Page fault – attempting to access a valid physical address for a page in the page table but the page is only
stored on disk.
Attempts to improve processor performance by having multiple processor components or functional units
simultaneously executing instructions.
Pipelining - functional units are arranged in stages.
Multiple issue - multiple instructions can be simultaneously initiated.
Pipeling Example #2
Pipelining
Multiple Issue
Speculation
Hardware multithreading
There aren’t always good opportunities for simultaneous execution of different threads.
Hardware multithreading provides a means for systems to continue doing useful work when the task being
currently executed has stalled. The current task has to wait for data to be loaded from memory.
o Ex., the current task has to wait for data to be loaded from memory.
Simultaneous multithreading (SMT) - a variation on fine-grained multithreading.
Allows multiple threads to make use of the multiple functional units
Fine-grained - the processor switches between threads after each instruction, skipping threads that are
stalled.
o Pros: potential to avoid wasted machine time due to stalls.
o Cons: a thread that’s ready to execute a long sequence of instructions may have to wait to execute
every instruction.
Coarse-grained - only switches threads that are stalled waiting for a time consuming operation to complete.
o Pros: switching threads doesn’t need to be nearly instantaneous.
o Cons: the processor can be idled on shorter stalls, and thread switching will also cause delays.
8
Flynn’s
Taxonomy
Example
SIMD drawbacks
All ALUs are required to execute the same instruction, or remain idle.
In classic design, they must also operate synchronously.
The ALUs have no instruction storage.
Efficient for large data parallel problems, but not other types of more complex parallel problems.
Vector processors
Operate on arrays or vectors of data while conventional CPU’s operate on individual data elements or scalars.
Vector registers: Capable of storing a vector of operands and operating simultaneously on their contents.
Vectorized and pipelined functional units: The same operation is applied to each element in the vector
(or pairs of elements).
Vector instructions: Operate on vectors rather than scalars.
Interleaved memory.
o Multiple “banks” of memory, which can be accessed more or less independently.
o Distribute elements of a vector across multiple banks, so reduce or eliminate delay in loading/storing
successive elements.
Strided memory access and hardware scatter/gather.
o The program accesses elements of a vector located at fixed intervals.
Fast.
Easy to use.
Vectorizing compilers are good at identifying code to exploit.
Compilers also can provide information about code that cannot be vectorized.
o Helps the programmer re-evaluate code.
High memory bandwidth.
Uses every item in a cache line.
They don’t handle irregular data structures as well as other parallel architectures.
A very finite limit to their ability to handle ever larger problems. (scalability)
9
Real time graphics application programming interfaces or API’s use points, lines, and triangles to internally
represent the surface of an object.
A graphics processing pipeline converts the internal representation into an array of pixels that can be sent to a
computer screen.
Several stages of this pipeline (called shader functions) are programmable. Typically just a few lines of C code.
Shader functions are also implicitly parallel, since they can be applied to multiple elements in the graphics
stream.
GPU’s can often optimize performance by using SIMD parallelism.
The current generation of GPU’s use SIMD parallelism. Although they are not pure SIMD systems.
UMA: Time to access all the memory locations will be the same for all the cores.
NUMA: A memory location a core is directly connected to can be accessed faster than a memory location that
must be accessed through another chip.
Interconnection network
Bus interconnect: A collection of parallel communication wires together with some hardware that controls
access to the bus. Communication wires are shared by the devices that are connected to it. As the number of
devices connected to the bus increases, contention for use of the bus increases, and performance decreases.
Switched interconnect: Uses switches to control the routing of data among the connected devices.
Crossbar: Allows simultaneous communication among different devices. Faster than buses. But the cost of
the switches and links is relatively high. BELOW WE HAVE CROSSBARS
Two groups
Bandwidth: The rate at which a link can transmit data. Usually given in megabits or megabytes per second.
Bisection bandwidth: A measure of network quality. Instead of counting the number of links joining the halves, it
sums the bandwidth of the links.
Hypercube
Indirect interconnects
More definitions: Any time data is transmitted, we’re interested in how long
it will take for the data to reach its destination.
Cache coherence: Programmers have no control over caches and when they get updated.
Uses a data structure called a directory that stores the status of each cache line.
When a variable is updated, the directory is consulted, and the cache controllers of the cores that have that
variable’s cache line in their caches are invalidated.
Shared Memory
Dynamic threads
o Master thread waits for work, forks new threads, and when threads are done, they terminate
o Efficient use of resources, but thread creation and termination is time consuming.
Static threads
o Pool of threads created and are allocated work, but do not terminate until cleanup.
o Better performance, but potential waste of system resources.
Nondeterminism
Race condition
Critical section
Mutually exclusive
Mutual exclusion lock (mutex, or simply lock)
busy-waiting message-passing
In distributed memory programs, only process 0 will access stdin. In shared memory programs, only the
master thread or thread 0 will access stdin.
In both distributed memory and shared memory programs all the processes/threads can access stdout and
stderr.
However, because of the indeterminacy of the order of output to stdout, in most cases only a single
process/thread will be used for all output to stdout other than debugging output.
Debug output should always include the rank or id of the process/thread that’s generating the output.
Only a single process/thread will attempt to access any single file other than stdin, stdout, or stderr. So, for
example, each process/thread can open its own, private file for reading or writing, but no two
processes/threads will open the same file.
Serial runtime of a program is the time elapsed between the beginning and the end of its execution on a
sequential computer.
The parallel runtime is the time that elapses from the moment the first processor starts to the moment the last
processor finishes execution.
We denote the serial runtime by TS or TSerial and the parallel runtime by TP or TParallel
Speedup
Number of cores = p
Serial run-time = Tserial
Parallel run-time = Tparallel
Tparallel = Tserial / p (linear speedup)
Speedup (S) is the ratio of the time taken to solve a problem on a single processor to the time required to solve the
𝑇
same problem on a parallel computer with p identical processing elements. 𝑆 = 𝑠𝑒𝑟𝑖𝑎𝑙
𝑇𝑝𝑎𝑟𝑎𝑙𝑙𝑒𝑙
Speedup Bounds
Superlinear Speedups
One reason for superlinearity is that the parallel version does less work than
corresponding serial algorithm.
Superlinear Speedups
Resource-based superlinearity: The higher aggregate cache/memory bandwidth can result in better
cache-hit ratios, and therefore superlinearity.
Example: A processor with 64KB of cache yields an 80% hit ratio. If two processors are used, since the
problem size/processor is smaller, the hit ratio goes up to 90%. Of the remaining 10% access, 8% come from
local memory and 2% from remote memory.
If DRAM access time is 100 ns, cache access time is 2 ns, and remote memory access time is 400ns, this
corresponds to a speedup of 2.43!
Efficiency is a measure of the fraction of time for which a processing element is usefully employed.
𝑆
Mathematically, it is given by: 𝐸 =
𝑃
Following the bounds on speedup, efficiency can be as low as 0 and as high as 1.
Speedup Efficiency
16
Amdahl’s Law: Unless virtually all of a serial program is parallelized, the possible speedup is going to be very
limited, regardless of the number of cores available. Thus Amdahl’s law provides an upper bound on the speedup that
can be obtained by a parallel program.
Let f be the fraction of operations in a computation that must be performed sequentially, where 0 ≤ f ≤ 1.
The maximum speedup achievable by a parallel computer with n processors is
The last expression is obtained by dividing both numerator and denominator by Ts.
Multiplying numerator & denominator by n produces the following alternate version of this formula:
Example 1
95% of a program’s execution time occurs inside a loop that can be executed in
parallel(while the other 5% is serial). What is the maximum speedup we should
expect from a parallel version of the program executing on 8 CPUs?
Example 2
Scalability
Taking Timings
Foster’s methodology
1. Partitioning: divide the computation to be performed and the data operated on by the computation into
small tasks. The focus here should be on identifying tasks that can be executed in parallel.
2. Communication: determine what communication needs to be carried out among the tasks identified in
the previous step.
3. Agglomeration or aggregation: combine tasks and communications identified in the first step into
larger tasks. For example, if task A must be executed before task B can be executed, it may make sense to
aggregate them into a single composite task.
4. Mapping: assign the composite tasks identified in the previous step to processes/[Link] should be
done so that communication is minimized, and each process/thread gets roughly the same amount of
work.
Histogram example
Concluding Remarks:
Serial systems: The standard model of computer hardware has been the von Neumann architecture.
Parallel hardware: Flynn’s taxonomy.
Parallel software:
o We focus on software for homogeneous MIMD systems, consisting of a single program that obtains
parallelism by branching.
o SPMD programs.
Input and Output
o We’ll write programs in which one process or thread can access stdin, and all processes can access
stdout and stderr.
o However, because of nondeterminism, except for debug output we’ll usually have a single process or
thread accessing stdout
Performance
o Speedup
o Efficiency
o Amdahl’s law
o Scalability
Parallel Program Design: Foster’s methodologyss or thread accessing stdout.
Contention
One-to-all broadcast
Input:
Output:
Ring Mesh
Ring
Recursive doubling
I Double the number of active processes in each step
Mesh
Hypercube
Algorithm
All-to-one reduction
Algorithm
All-to-all broadcast
Ring
Ring algorithm
Number of steps: p − 1
Time per step: ts + twm
Total time: (p − 1)(ts + twm)
Mesh algorithm
Hypercube algorithm
All-to-all reduction
Input:
Output:
The “sum” Mr:= M0,r ⊕ M1,r ⊕ · · · ⊕ Mp-1,r stored locally on each process r
Algorithm
All-reduce
Input:
Output:
Algorithm
Prefix sum
Input:
Output:
Algorithm
Scatter
Input:
Output:
Algorithm
Gather
Input:
Output:
Algorithm
All-to-all personalized
Input:
Output:
Summary
E-cube routing
Time analysis
Time analysis
Improved all-reduce
All-reduce = One-to-all reduction + All-to-one broadcast
Time analysis
POSIX®Threads
Caveat: The Pthreads API is only available onPOSIXR systems — Linux, MacOS X,Solaris, HPUX, …
Hello World(3)
Compiling a Pthread program
Global variables
pthread_t objects
Opaque
The actual data that they store is system-specific.
Their data members aren’t directly accessible to user code.
However, the Pthreads standard guarantees that a pthread_t object does store enough information to uniquely
identify the thread with which it’s associated.
27
Serial pseudo-code
Using 3 Pthreads
Pthreads global sum with busy-waiting Global sum function with critical section after loop (1)
Mutexes
A thread that is busy-waiting may continually use the CPU accomplishing nothing.
Mutex (mutual exclusion) is a special type of variable that can be used to restrict access to a critical
section to a single thread at a time.
Used to guarantee that one thread“excludes” all other threads while itexecutes the critical section.
The Pthreads standard includes a special type for mutexes: pthread_mutex_t.
When a thread is finished executing the code in a critical section, it should call
Global sum function that uses a mutex(1) Global sum function that uses a mutex(2)
Issues
Problems with a mutex solution A first attempt at sending messages using pthreads
Condition Variables
Linked Lists
In order to share access to the list, we can define head_p to be a global variable.
This will simplify the function headers for Member, Insert, and Delete, since we won’t need to pass in either
head_p or a pointer to head_p: we’ll only need to pass in the value of interest.
Simultaneous access
by two threads
Solution #1
Issues of Sol #1
Solution #2
Instead of locking the entire list, we could try to lock individual nodes.
A “finer-grained” approach.
Issues of Sol #2
Neither of our multi-threaded linked lists exploits the potential for simultaneous access to any node by threads
that are executing Member.
The first solution only allows one thread to access the entire list at any instant.
The second only allows one thread to access any given node at any instant.
A read-write lock is somewhat like a mutex except that it provides two lock functions.
The first lock function locks the read-writelock for reading, while the second locks itfor writing.
So multiple threads can simultaneously obtain the lock by calling the read-lock function, while only one thread
can obtain the lock by calling the write-lock function.
Thus, if any threads own the lock for reading, any threads that want to obtain the lock for writing will block in
the call to the write-lock function.
If any thread owns the lock for writing, any threads that want to obtain the lock for reading or writing will
block in their respective locking functions.
100,000 ops/thread
99.9% Member
0.05% Insert
0.05% Delete
100,000 ops/thread
80% Member
10% Insert
10% Delete
Caches, Cache-Coherence, and False Sharing
Recall that chip designers have added blocks of relatively fast memory to processors called cache memory.
The use of cache memory can have a huge impact on shared-memory.
A write-miss occurs when a core tries to update a variable that’s not in cache, and it has to access main
memory.
Thread-Safety
Example
Suppose we want to use multiple threads to “tokenize” a file that consists of ordinary English text.
The tokens are just contiguous sequences of characters separated from the rest of the text by white-space — a
space, a tab, or a newline.
34
Simple approach
Divide the input file into lines of text and assign the lines to the threads in a roundrobin fashion.
The first line goes to thread 0, the second goes to thread 1, . . . , the tth goes to thread t, the t +1st goes to
thread 0, etc.
We can serialize access to the lines ofinput using semaphores.
After a thread has read a single line ofinput, it can tokenize the line using the strtok function.
The first time it’s called the string argument should be the text to be tokenized. Our line of input.
For subsequent calls, the first argument should be NULL.
The idea is that in the first call, strtok caches a pointer to string, and for subsequent calls it returns successive
tokens taken from the cached copy.
Concluding remarks
OpenMP
Pragmas (#pragma)
OpenMP pragmas
Of note…
There may be system-defined limitations on the number of threads that a program can start.
The OpenMP standard doesn’t guarantee that this will actually start thread_count threads.
Most current systems can start hundreds or even thousands of threads.
Unless we’re trying to start a lot of threads, we will almost always get the desired number of threads.
Some terminology: In OpenMP parlance the collection of threads executing the parallel block — the original thread
and the new threads — is called a team, the original thread is called the master, and the additional threads are called
slaves.
Mutual exclusion
# pragma omp critical <- only one thread can execute the following structured block at a time
global_result += my_result ;
Scope
2. In serial programming, the scope of a variable consists of those parts of a program in which the variable can be
used.
3. In OpenMP, the scope of a variable refers to the set of threads that can access the variable in a parallel block.
Scope in OpenMP
A variable that can be accessed by all the threads in the team has shared scope.
A variable that can only be accessed by a single thread has private scope.
The default scope for variables declared before a parallel block is shared.
Reduction operators
Parallel for
Caveats
The variable index must have integer or pointer type (e.g., it can’t be a float).
The expressions start, end, and incr must have a compatible type. For example, if index is a pointer, then incr
must have integer type.
The expressions start, end, and incr must not change during execution of the loop.
During execution of the loop, the variable index can only be modified by the “increment expression” in the for
statement.
39
Data dependencies
What happened?
OpenMP solution #2
Bubble Sort
Second OpenMP Odd-Even Sort Odd-even sort with two parallel for directives and two for
directives. (Times are in seconds.)
Scheduling loops
Results
The iterations are also broken up into chunksof chunksize consecutive iterations.
Each thread executes a chunk, and when a thread finishes a chunk, it requests another one from the run-time
system.
This continues until all the iterations are completed.
The chunksize can be omitted. When it is omitted, a chunksize of 1 is used.
Each thread also executes a chunk, and when a thread finishes a chunk, it requests another one.
However, in a guided schedule, as chunks are completed the size of the new chunks decreases.
If no chunksize is specified, the size of the chunks decreases down to 1.
If chunksize is specified, it decreases down to chunksize, with the exception that the very last chunk can be
smaller than chunksize.
The system uses the environment variable OMP_SCHEDULE to determine at run time how to schedule the
loop.
The OMP_SCHEDULE environment variable can take on any of the values that can be used for a static,
dynamic, or guided schedule.
Queues
Can be viewed as an abstraction of a line of customers waiting to pay for their groceries in a supermarket.
A natural data structure to use in many multithreaded applications.
For example, suppose we have several “producer” threads and several “consumer” threads.
o Producer threads might “produce” requests for data.
o Consumer threads might “consume” the request by finding or generating the requested data.
Message-Passing
Sending Messages
Receiving Messages
42
Termination Detection
Startup
Unlike the critical directive, it can only protect critical sections that consist of a single C assignment statement.
o # pragma omp atomic
Further, the statement must have one of the following forms:
o <op>=<expression>;
o x++;
o ++x;
o x--;
o --x;
Here <op> can be one of the binary operators
o +, *, -, /, &, ^, |, <<, or >>
Many processors provide a special load-modify-store instruction.
A critical section that only does a load-modify-store can be protected much more efficiently by using this
special instruction rather than the constructs that are used to protect more general critical sections.
Critical Sections
Locks: A lock consists of a data structure and functions that allow the
programmer to explicitly enforce mutual exclusion in a critical section.
Some Caveats
1. You shouldn’t mix the different types of mutual exclusion for a single critical section.
2. There is no guarantee of fairness in mutual exclusion constructs.
3. It can be dangerous to “nest” mutual exclusion constructs
Concluding Remarks
Compilation Execution
MPI Programs
Written in C.
o Has main.
o Uses stdio.h, string.h, etc.
Need to add mpi.h header file.
Identifiers defined by MPI start with “MPI_”.
First letter following underscore is uppercase.
o For function names and MPI-defined types.
o Helps to avoid confusion.
MPI Components
MPI_Init
o Tells MPI to do all the necessary setup.
MPI_Finalize
o Tells MPI we’re done, so clean up anything allocated for this program.
Communicators
SPMD
Single-Program Multiple-Data
We compile one program.
Process 0 does something different.
o Receives messages and prints them while the other processes do the work.
The if-else construct makes our program SPMD.
45
Communication
Receiving messages
Parallel pseudo-code
Tree-structured communication
(c) Processes 2 and 6 send their new values to processes 0 and 4, respectively.
(d) Processes 0 and 4 add the received values into their new values.
All the processes in the communicator must call the same collective function.
For example, a program that attempts to match a call to MPI_Reduce on one process with a call to MPI_Recv
on another process is erroneous, and, in all likelihood, the program will hang or crash.
The arguments passed by each process to an MPI collective communication must be “compatible.”
For example, if one process passes in 0 as the dest_process and another passes in 1, then the outcome of a call
to MPI_Reduce is erroneous, and, once again, the program is likely to hang or crash.
The output_data_p argument is only used on dest_process.
However, all of the processes still need to pass in an actual argument corresponding to output_data_p, even if
it’s just NULL.
Point-to-point communications are matched on the basis of tags and communicators.
Collective communications don’t use tags.
They’re matched solely on the basis of the communicator and the order in which they’re called.
Example 1
Multiple calls to
MPI_Reduce
Example 2
Suppose that each process calls MPI_Reduce with operator MPI_SUM, and destination process 0.
At first glance, it might seem that after the two calls to MPI_Reduce, the value of b will be 3, and the value of d
will be 6.
However, the names of the memory locations are irrelevant to the matching of the calls to MPI_Reduce.
The order of the calls will determine the matching so the value stored in b will be 1+2+1 = 4, and the value
stored in d will be 2+1+2 = 5.
MPI_Allreduce
Useful in a situation in which all of the processes need the result of a global sum in order to complete some larger
computation.
49
Broadcast: Data belonging to a single process is sent to all of the processes in the communicator.
Partitioning options
Block partitioning
o Assign blocks of consecutive components to each process.
Cyclic partitioning
o Assign components in a round robin fashion.
Block-cyclic partitioning
o Use a cyclic distribution of blocks of components.
Gather
Allgather
Matrix-vector multiplication
Derived datatypes
Used to represent any collection of data items in memory by storing both the types of the items and their
relative locations in memory.
The idea is that if a function that sends data knows this information about a collection of data items, it can
collect the items from memory before they are sent.
Similarly, a function that receives data can distribute the items into their correct destinations in memory when
they’re received.
Formally, consists of a sequence of basic MPI data types
together with a displacement for each of the data types.
Trapezoidal Rule example:
52
MPI_Type create_struct
MPI_Get_address
MPI_Type_commit
Allows the MPI implementation to optimize its internal representation of the datatype for use in
communication functions.
MPI_Type_free
Get input function with a derived datatype (1) Get input function with a derived datatype (2)
MPI_Barrier
Ensures that no process will return from calling it until every process in the communicator has started calling it.
Speedup
Efficiency
Scalability
A program is scalable if the problem size can be increased at a rate so that the efficiency doesn’t decrease as
the number of processes increase.
Programs that can maintain a constant efficiency without increasing the problem size are sometimes said to be
strongly scalable.
Programs that can maintain a constant efficiency if the problem size increases at the same rate as the number
of processes are sometimes said to be weakly scalable.
Sorting
A sequence of phases.
Even phases, compare swaps:
Example
Start: 5, 9, 4, 3
Even phase: compare-swap (5,9) and (4,3) getting the list 5, 9, 3, 4
Odd phase: compare-swap (9,3) getting the list 5, 3, 9, 4
Even phase: compare-swap (5,3) and (9,4) getting the list 3, 5, 4, 9
Odd phase: compare-swap (5,4) getting the list 3, 4, 5, 9
54
Compute_partner
MPI_Ssend
Restructuring communication
MPI_Sendrecv
Concluding Remarks
MPI or the Message-Passing Interface is a library of functions that can be called from C, C++, or Fortran
programs.
A communicator is a collection of processes that can send messages to each other.
Many parallel programs use the single-program multiple data or SPMD approach.
Most serial programs are deterministic: if we run the same program with the same input we’ll get the same
output.
Parallel programs often don’t possess this property.
Collective communications involve all the processes in a communicator.
When we time parallel programs, we’re usually interested in elapsed time or “wall clock time”.
Speedup is the ratio of the serial run-time to the parallel run-time.
Efficiency is the speedup divided by the number of parallel processes.
If it’s possible to increase the problem size (n) so that the efficiency doesn’t decrease as p is increased, a
parallel program is said to be scalable.
An MPI program is unsafe if its correct behavior depends on the fact that MPI_Send is buffering its input.