NUMERICAL PARALLEL COMPUTING

Similar documents
Parallel Programming. OpenMP Parallel programming for multiprocessors for loops

OpenMP - II. Diego Fabregat-Traver and Prof. Paolo Bientinesi WS15/16. HPAC, RWTH Aachen

Programming Shared Memory Systems with OpenMP Part I. Book

Parallel Programming. Exploring local computational resources OpenMP Parallel programming for multiprocessors for loops

OpenMP. Diego Fabregat-Traver and Prof. Paolo Bientinesi WS16/17. HPAC, RWTH Aachen

Scientific Computing

OpenMP. Application Program Interface. CINECA, 14 May 2012 OpenMP Marco Comparato

Topics. Introduction. Shared Memory Parallelization. Example. Lecture 11. OpenMP Execution Model Fork-Join model 5/15/2012. Introduction OpenMP

Allows program to be incrementally parallelized

Parallel Programming in C with MPI and OpenMP

Multi-core Architecture and Programming

OpenMP 2. CSCI 4850/5850 High-Performance Computing Spring 2018

Q6B8xqZ8n8bwjGdzBJ25X2utwnoEG

Module 10: Open Multi-Processing Lecture 19: What is Parallelization? The Lecture Contains: What is Parallelization? Perfectly Load-Balanced Program

OpenMP. OpenMP. Portable programming of shared memory systems. It is a quasi-standard. OpenMP-Forum API for Fortran and C/C++

Introduction to OpenMP. OpenMP basics OpenMP directives, clauses, and library routines

OpenMP I. Diego Fabregat-Traver and Prof. Paolo Bientinesi WS16/17. HPAC, RWTH Aachen

Data Environment: Default storage attributes

Introduction to. Slides prepared by : Farzana Rahman 1

Multithreading in C with OpenMP

OpenMP. Dr. William McDoniel and Prof. Paolo Bientinesi WS17/18. HPAC, RWTH Aachen

Parallel Programming in C with MPI and OpenMP

Lecture 4: OpenMP Open Multi-Processing

Parallel Programming in C with MPI and OpenMP

Distributed Systems + Middleware Concurrent Programming with OpenMP

OpenMP C and C++ Application Program Interface Version 1.0 October Document Number

COSC 6374 Parallel Computation. Introduction to OpenMP. Some slides based on material by Barbara Chapman (UH) and Tim Mattson (Intel)

Parallel and Distributed Computing

High Performance Computing: Tools and Applications

Introduction to OpenMP.

OpenMP Algoritmi e Calcolo Parallelo. Daniele Loiacono

Programming with Shared Memory PART II. HPC Fall 2007 Prof. Robert van Engelen

Parallel Programming with OpenMP. CS240A, T. Yang

15-418, Spring 2008 OpenMP: A Short Introduction

EE/CSCI 451: Parallel and Distributed Computation

1 of 6 Lecture 7: March 4. CISC 879 Software Support for Multicore Architectures Spring Lecture 7: March 4, 2008

Shared Memory Programming Models I

Introduction to OpenMP

EPL372 Lab Exercise 5: Introduction to OpenMP

CSL 860: Modern Parallel

Overview: The OpenMP Programming Model

CS 470 Spring Mike Lam, Professor. Advanced OpenMP

Programming with Shared Memory PART II. HPC Fall 2012 Prof. Robert van Engelen

Programming with OpenMP*

ECE 574 Cluster Computing Lecture 10

OpenMP - III. Diego Fabregat-Traver and Prof. Paolo Bientinesi WS15/16. HPAC, RWTH Aachen

Shared Memory Parallelism - OpenMP

Introduction to Standard OpenMP 3.1

Cluster Computing 2008

CS4961 Parallel Programming. Lecture 9: Task Parallelism in OpenMP 9/22/09. Administrative. Mary Hall September 22, 2009.

MPI and OpenMP (Lecture 25, cs262a) Ion Stoica, UC Berkeley November 19, 2016

Concurrent Programming with OpenMP

Parallel Programming using OpenMP

Parallel Programming using OpenMP

Introduction to OpenMP

Module 11: The lastprivate Clause Lecture 21: Clause and Routines. The Lecture Contains: The lastprivate Clause. Data Scope Attribute Clauses

UvA-SARA High Performance Computing Course June Clemens Grelck, University of Amsterdam. Parallel Programming with Compiler Directives: OpenMP

OpenMP programming. Thomas Hauser Director Research Computing Research CU-Boulder

HPC Practical Course Part 3.1 Open Multi-Processing (OpenMP)

Shared Memory Programming with OpenMP


OpenMP Programming. Prof. Thomas Sterling. High Performance Computing: Concepts, Methods & Means

Introduction to OpenMP

OpenMP 4.5: Threading, vectorization & offloading

COSC 6374 Parallel Computation. Introduction to OpenMP(I) Some slides based on material by Barbara Chapman (UH) and Tim Mattson (Intel)

CS 470 Spring Mike Lam, Professor. OpenMP

Chapter 3. OpenMP David A. Padua

Parallel Computing. Prof. Marco Bertini

CS 470 Spring Mike Lam, Professor. OpenMP

A Short Introduction to OpenMP. Mark Bull, EPCC, University of Edinburgh

OpenMP Overview. in 30 Minutes. Christian Terboven / Aachen, Germany Stand: Version 2.

Shared Memory Parallelism using OpenMP

Synchronization. Event Synchronization

OpenMP. António Abreu. Instituto Politécnico de Setúbal. 1 de Março de 2013

Alfio Lazzaro: Introduction to OpenMP

EE/CSCI 451 Introduction to Parallel and Distributed Computation. Discussion #4 2/3/2017 University of Southern California

Computational Mathematics

Parallel Programming

Concurrent Programming with OpenMP

Shared Memory Programming Paradigm!

by system default usually a thread per CPU or core using the environment variable OMP_NUM_THREADS from within the program by using function call

OpenMP Tutorial. Rudi Eigenmann of Purdue, Sanjiv Shah of Intel and others too numerous to name have contributed content for this tutorial.

High Performance Computing: Tools and Applications

Advanced C Programming Winter Term 2008/09. Guest Lecture by Markus Thiele

Barbara Chapman, Gabriele Jost, Ruud van der Pas

Parallelising Scientific Codes Using OpenMP. Wadud Miah Research Computing Group

CS691/SC791: Parallel & Distributed Computing

OpenMP 4.0/4.5: New Features and Protocols. Jemmy Hu

ECE/ME/EMA/CS 759 High Performance Computing for Engineering Applications

Parallel programming using OpenMP

COMP Parallel Computing. SMM (2) OpenMP Programming Model

Open Multi-Processing: Basic Course

OpenMP Tutorial. Seung-Jai Min. School of Electrical and Computer Engineering Purdue University, West Lafayette, IN

Introduction to OpenMP

Jukka Julku Multicore programming: Low-level libraries. Outline. Processes and threads TBB MPI UPC. Examples

Parallel Processing Top manufacturer of multiprocessing video & imaging solutions.

OpenMP Introduction. CS 590: High Performance Computing. OpenMP. A standard for shared-memory parallel programming. MP = multiprocessing

Session 4: Parallel Programming with OpenMP

Introduction to OpenMP. Lecture 4: Work sharing directives

Review. Lecture 12 5/22/2012. Compiler Directives. Library Functions Environment Variables. Compiler directives for construct, collapse clause

Transcription:

Lecture 4: More on OpenMP http://people.inf.ethz.ch/iyves/pnc11/ Peter Arbenz, Andreas Adelmann Computer Science Dept, ETH Zürich, E-mail: arbenz@inf.ethz.ch Paul Scherrer Institut, Villigen E-mail: andreas.adelmann@psi.ch arallel Numerical Computing. Lecture 4, Mar 18, 2012 1/50

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 2/50 OpenMP: An application programming interface (API) that provides a parallel programming model for shared memory multiprocessors. It extends the programming languages C/C++ and Fortran by a set of compiler directives (called pragmas) to express shared memory parallelism. a set of runtime library routines and environment variables to examine and modify execution parameters.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 3/50 (cont.) Creation of multiple threads with the parallel pragma: #pragma omp parallel [clause [clause]... ] The parallel directive is used to create multiple threads of execution that execute concurrently. The master thread and its execution context exist for the duration of the entire program. If the master thread arrives at a parallel directive it spawns some new threads and forms a team of treads. The parallel directive applies to a structured block, i.e. a block of code with one entry point at the top and one exit point at the bottom of the block (parallel region). At the end of the parallel section the execution is joined again in the single master thread. An implicit barrier forces all threads to wait until all have completed their work.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 4/50 (cont.) cf. Chandra et al.: Parallel Programming in OpenMP

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 5/50 (cont.) see http://en.wikipedia.org/wiki/openmp

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 6/50 (cont.) Work-sharing constructs divide work among threads #pragma omp for [clause [clause]... ] Distribute iterations of a for-loop over the threads. #pragma omp sections [clause [clause]... ] Distribute independent work units. #pragma omp single Only one thread executes the code block. Work-sharing constructs do not launch new threads. There is an implicit barrier at the end of the construct. (Cf. nowait clause) If work-sharing constructs occur outside a parallel region, they are ignored. (Function called inside/outside parallel region.)

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 7/50 (cont.) #pragma omp for [clause [clause]... ] Most important work-sharing construct that implements loop-level parallelism; distributes iterations of a for-loop among threads. Schedule clauses determine how loop iterations are mapped onto threads schedule(static [,chunk]) Deal out blocks of iterations of size chunk to each thread. schedule(dynamic [,chunk]) Each thread grabs chunk iterations off a queue until all iterations have been handled schedule(guided [,chunk]) Dynamic schedule with variable (decreasing) chunk sizes.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 8/50 Combined work-sharing loop constructs #pragma omp parallel for [clause [clause]... ] #pragma omp parallel sections [clause [clause]... ] Combined parallel work-sharing constructs are shortcuts that can be used when a parallel region comprises exactly one work-sharing construct, i.e., the work-sharing construct contains all the code in the parallel region.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 9/50 Parallelizing simple loops In order for the compiler to successfully transform the sequential loop into a parallel loop, it must be able to determine the number of loop iterations. The control clause of the for loop #pragma omp parallel for for (idx=first; idx op last; increment){ assignments; } must be in canonical shape (see next page). Neither the loop counter idx nor the loop boundaries must be altered in the loop. There must not be a break in the loop. A continue statement is allowed.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 10/50 Canonical shape for (idx=start; idx < <= >= > end; idx++ ++idx idx-- --idx idx += inc idx -= inc idx = idx + inc idx = inc + idx idx = idx - inc ) In order to be made parallel, the control clause of a for loop must have canonical shape. Above you see the legal variants. The identifiers start, end, and inc may be expressions.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 11/50 Accessing variables / communicating among threads Accessing variables / communicating among threads The threads of a team of threads are executed in a shared address space. In in the parallel section of the saxpy example below all threads can access all variables. #pragma omp parallel for schedule(static,chunk_size) for (i=0; i< N; i++) y[i] = alpha*x[i] + y[i]; In this way, communication among threads is very easy: A thread simply writes in a shared variable where it can be accessed by other threads. This behavior is not always desired. Therefore, OpenMP allows to define so-called private variables that are assigned exclusively to a thread. The scope of such variables is limited to that thread.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 12/50 Accessing variables / communicating among threads Shared vs. private vs. reduction variables A shared variable has a single storage location in memory for the whole duration of the parallel construct. All threads that reference such a variable will access the same memory location. Thus, reading and writing such a variable provides an easy mechanism for communicating between threads. A private variable has multiple storage locations, one within the execution context of each thread. This holds for pointers as well. Threads are not allowed to mess with the private memory of other threads. Therefore, do not assign to a private pointer the address of a private variable of another thread. The result is not defined.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 13/50 Accessing variables / communicating among threads Shared vs. private vs. reduction variables (cont.) The content of private variables at entry / at exit of its domain of definition is not defined. The clauses firstprivate / lastprivate allow to initialize / finalize their values. In C/C++, by default, all program variables except the loop index become shared variables in a parallel region. By the clause default(shared) or default(none) this can be overwritten. Variables that are defined within the scope of a parallel pragma are private to this thread. (stack-allocated) There is a further type of variables: a reduction variable is used in reduction operations for accumulating values across threads, see a later example.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 14/50 Accessing variables / communicating among threads General properties of data scope clauses A data scope clause consists of the keyword identifying the clause, followed by a comma-separated list of variables within parenthesis. private(x,y) shared(z) private(alpha,beta) Any variable can be marked with a data scope clause (automatic, global, or formal parameters to subroutines) However, the directive with the scope clause must be in the lexical (static) extent of the declaration of each of the variables named in the scope clause. A data scoping clause applies to whole objects not to elements. An individual variable can appear at most once in a clause. The clause applies to precisely the directive it belongs to.

Private variable initialization and finalization Private variable initialization and finalization Private variables live only in parallel sections. If a variable named var that appears in the private clause is used in the sequential portion of the code as well then the variable in the parallel region is stored in a different memory location. The private variable var has no defined value at the beginning of the parallel region! If the value of the global variable var should be inherited by the private variable var the latter is initialized by this value by #pragma omp parallel firstprivate(var) In a similar way #pragma omp parallel lastprivate(var) transfers the value of the private copy var in the sequentially last iteration of the loop to the variable var in the master thread. Parallel Numerical Computing. Lecture 4, Mar 18, 2012 15/50

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 16/50 Private variable initialization and finalization void wrong() {... is = 0; #pragma omp parallel for private(is) for (j=0; j < 1000; j++) is = is +... ; } printf("%d",is); Remarks: Inside the loop is is not initialized. Regardless of initialization, is is undefined in the print statement. Example from T. Mattson (Intel): Introduction to OpenMP.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 17/50 Private variable initialization and finalization void closer() {... is = 0; #pragma omp parallel for firstprivate(is) for (j=0; j < 1000; j++) is = is +... ; } printf("%d",is); Remarks: In each thread, is is initialized to 0. is is still undefined in the print statement.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 18/50 Private variable initialization and finalization void even_closer() {... is = 0; #pragma omp parallel for firstprivate(is) lastprivate(is) for (j=0; j < 1000; j++) is = is +... ; } printf("%d",is); Remark: is gets the value it had in the thread that executed the last iteration (j=999) of the loop. This may not be the desired value.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 19/50 OpenMP synchronization OpenMP synchronization Threads can easily communicate with each other through reads and writes of shared variables. However, this has to be done with caution. Two forms of process synchronization or coordination are mutual exclusion OpenMP provides a critical directive to provide a thread-exclusive access to a shared variable. event synchronization The barrier directive is a simple means to synchronize threads. A barrier defines a point where each thread waits for all other threads to arrive. Remember that there is an implicit barrier at the end of each parallel and work-sharing construct.

omp_set_num_threads(num_threads); #pragma omp parallel for for (i=0; i< N; i++){ y[i] = 1.0; x[i] = (float)i; }; arallel Numerical Computing. Lecture 4, Mar 18, 2012 20/50 Example: dot product Example: dot product #include <stdio.h> #include <omp.h> #define N 1000 #define NUM_THREADS 4 main() { int i; float sum, x[n], y[n];

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 21/50 Example: dot product /* Now comes the interesting part of the code */ sum = 0.0; #pragma omp parallel for for (i=0; i< N; i++){ sum = sum + x[i]*y[i]; } } printf("%f\n", sum); What is the problem here? Let this code run several (many) times. Unintended sharing of variables can lead to race conditions: the results (may) change as the threads are scheduled differently.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 22/50 Example: dot product So, the problem is the uncontrolled reading and writing of the shared variable sum. So, let s protect this operation. Version II: sum = 0.0; #pragma omp parallel for #pragma omp critical { sum = sum + x[i]*y[i]; } } Have things improved? What about the cost of synchronization?

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 23/50 Example: dot product version p = 1 2 4 6 8 12 16 I 1875 4321 8799 8105 6348 7339 6538 II 155 547 121 387 139 795 140 576 171 973 1 052 832 3 541 113 III 1387 728 381 264 225 176 93 IV 1392 732 381 269 220 176 88 Table 1: Some execution times in µsec for the dot product Numbers from Peterson & Arbenz.

Example: dot product Version III: main() { int i; float sum, local_sum, x[n], y[n];... sum = 0.0; #pragma omp parallel private(local_sum) { local_sum = 0.0; #pragma omp for for (i=0; i< N; i++) local_sum = local_sum + x[i]*y[i]; #pragma omp critical sum = sum + local_sum; } This is much better. There are only NUM THREADS sync points. Parallel Numerical Computing. Lecture 4, Mar 18, 2012 24/50

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 25/50 Parallel reduction operations Parallel reduction operations The most elegant way how to deal with the above problem is to give the variable sum the reduction attribute. Version IV: sum = 0.0; #pragma omp parallel for reduction(+ : sum) for (i=0; i< N; i++){ sum = sum + x[i]*y[i]; } Here, the compiler implicitly creates a local copy of sum. At the end of the loop, the local copies are added (in an optimal way) to yield the desired result.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 26/50 Parallel reduction operations Parallel reduction operations (cont.) Reductions are so common that OpenMP allows us to add a reduction clause to the parallel for pragma. OpenMP takes care of the details, such as storing partial sums in private variables and then adding the partial sums to the shared variable after the loop. Syntax: reduction (red op : var list) Reduction operations are possible with the operators +, -, *, &,, ^, &&,. The initial values are the most meaningful ones. Admitted variable types are the basic data types in C/C++.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 27/50 Parallel reduction operations Critical sections in OpenMP The syntax of critical sections in OpenMP is: #pragma omp critical [(name)] {... } The critical directive marks the subsequent block of code as critical. The OpenMP runtime environment guarantees that at any given time the block of code is executed by at most one thread.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 28/50 Parallel reduction operations Critical sections in OpenMP (cont.) All code segments in a program that are indicated critical are treated as one single critical section! They cannot be accessed by more than one thread. This leads to the situation that threads at different locations in a program have to wait for each other although they need not be synchronized at all. To improve this situation and get finer granularity for synchronization, critical sections can be named. If critical sections are named then threads wait for entering a critical section until no other thread is inside the critical section with the same name.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 29/50 Parallel reduction operations Deadlocks A deadlock is a situation wherein two or more competing actions are waiting for the other to finish, and thus neither ever does. From Wikipedia: When two trains approach each other at a crossing, both shall come to a full stop and neither shall start up again until the other has gone. (Kansas law) Famous example of Dijkstra: The five dining philosophers problem.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 30/50 Parallel reduction operations Deadlocks (cont.) With threads: a deadlock is a situation where one or multiple threads have to wait infinitely long for an event to complete or a resource to become available and only the involved threads could complete the event or make available the resource. Example: A function is called in a critical section C 0. Inside the routine there is another critical section C 1 with the same name (or both unnamed). A thread that as entered C 0 will never be able to enter C 1. It waits entering C 1 until all threads (including itself) have left C 0 which is not possible since it would have to complete C 1 before returning into C 0.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 31/50 Data dependences Data dependences Whenever a statement in a program reads or writes a memory location and another statement reads or writes the same memory location, and at least one of the two statements writes the location, then there is a data dependence on that memory location between the two statements. Example: for (i=1; i<n; i++) {a[i] = a[i] + a[i-1];} Here, a[i] is written in loop iteration i and read in loop iteration i + 1. This loop cannot be executed in parallel. The results may not be correct. (Assume a[i]=1 for all i. The correct (serial) result would be a[i]=i+1. However, if the second iteration step reads a[1] before it is written in the first iteration step then a[1]=a[2]=2.)

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 32/50 Data dependences How to detect data dependences How to detect data dependences We have to analyze each variable: Is the variable only read and never assigned within the loop body? If so, there are no dependences involving it. Otherwise, consider the memory locations that make up the variable and that are assigned within the loop. For each such location, is there exactly one iteration that accesses the location? If so, there are no dependences involving the variable. If not, there is a dependence. In other words, if there is a dependence then there are two indices i j (in the bounds of the loop) such that iteration i assigns to some element of an array a, say, and iteration j reads or writes to that same element of a. Such a data dependence is called loop-carried.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 33/50 Data dependences Classification of data dependences Classification of data dependences Let the statement executed earlier in the sequential execution be loop S 1, and let the later statement be S 2. flow dependence: The memory location is written in S 1 and read in S 2. S 1 must execute before S 2 to produce the value that is consumed in S 2. anti-dependence: The memory location is read in S 1 and written in S 2. output dependence: The memory location is written in both statements S 1 and S 2.

#pragma omp parallel for shared(a, a2) lastprivate(x) for (i=0; i<n-1; i++) { x = (b[i] + c[i])/2; a[i] = a2[i] + x; } Parallel Numerical Computing. Lecture 4, Mar 18, 2012 34/50 Data dependences Removing flow dependences Anti-dependences and output dependences can be removed by auxiliary variables (arrays) or reductions Example, sequential version: for (i=0; i<n-1; i++) { x = (b[i] + c[i])/2; a[i] = a[i+1] + x; } Parallel version with dependences removed: #pragma omp parallel for shared(a, a2) for (i=0; i<n-1; i++) a2[i] = a[i+1];

Data dependences Removing flow dependences Flow dependences can in general not be removed. Let s consider the LU factorization of tridiagonal diagonally dominant matrix: a 0 c 0 b 1 a 1 c 1 b 2 a 2 c 2 b 3 a 3 c 3 b 4 a 4 c 4 b 5 a 5 1 d 0 c 0 l 1 1 d 1 c 1 l2 1 d = l3 1 2 c 2 d 3 c 3 l 4 1 d 4 c 4 l5 1 d 5 T = LU Parallel Numerical Computing. Lecture 4, Mar 18, 2012 35/50

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 36/50 Data dependences Removing flow dependences For solving T x = LUx = Lz = b, z = Ux, we proceed as follows (forward / backward substitution) Lz = b, Ux = z. In C the forward substitution Lz = b is z[0] = b[0]; for(i=1;i<n;i++){ z[i] = b[i] - l[i]*z[i-1]; } This is a very tight recurrence! Loop iteration i (S 2 ) reads z[i-1] which was written in loop iteration i-1 (S 1 ). To remove such dependences the algorithm must be changed.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 37/50 Case study 1: Matrix-vector multiplication The problem Case study: Matrix-vector multiplication Let us multiply a M-by-N matrix A with a N-vector x and store the result in the M-vector y y = Ax. Component-wise this is y j = N a ji x i. i=1

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 38/50 Case study 1: Matrix-vector multiplication Version I Version I: j,i, outer #pragma omp parallel for private(i) for (j=0; j<m; j++){ y[j] = 0.0; for (i=0; i<n; i++) y[j] = y[j] + A[j+i*M]*x[i]; } The picture shows the case where 5 threads are used. Each thread accesses another portion of the matrix/result vector (grey scale). The outer loop is parallelized. Note that the matrix is stored by columns.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 39/50 Case study 1: Matrix-vector multiplication Version II Version II: j,i / inner. with reduction variable for (j=0; j<m; j++){ tmp = 0.0; #pragma omp parallel for reduction(+ : tmp) for (i=0; i<n; i++) tmp = tmp + A[j+i*M]*x[i]; y[j] = tmp; }

Case study 1: Matrix-vector multiplication Version III Version III: i,j / outer. with vector reduction /critical section #pragma omp parallel private(j,z) { for (j=0; j<m; j++) z[j] = 0.0; #pragma omp for for (i=0; i<n; i++) for (j=0; j<m; j++){ z[j] = z[j] + A[j+i*M]*x[i]; } #pragma omp critical for (j=0; j<m; j++) y[j] = y[j] + z[j]; } arallel Numerical Computing. Lecture 4, Mar 18, 2012 40/50

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 41/50 Case study 1: Matrix-vector multiplication Version IV Version IV: i,j / inner for (i=0; i<n; i++){ #pragma omp parallel for for (j=0; j<m; j++) y[j] = y[j] + A[j+i*M]*x[i]; }

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 42/50 Case study 1: Matrix-vector multiplication Comparison / Timings Comparison / Timings Case p I II III IV V N = M = 500 4 1.2 20 0.1 20 0.25 8 0.6 38 0.1 27 0.35 N = M = 1000 4 5.0 43 0.50 46 0.75 2.5 73 0.25 80 0.75 Table 2: Some execution times in millisec for y = Ax Version V: outer, calling BLAS-2 dgemv routine.

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 43/50 Case study 2: Sparse matrix-vector multiplication The problem Case study 2: Sparse matrix-vector multiplication The same problem as before, but now the M-by-N matrix A is sparse. y = Ax. Component-wise this is y j = N a ji x i. i=1

arallel Numerical Computing. Lecture 4, Mar 18, 2012 44/50 Case study 2: Sparse matrix-vector multiplication Storing of sparse matrices Storing of sparse matrices >> full(a) >> A ans = A = 1 0 0 0 0 (1,1) 1 0 2 5 0 0 (2,2) 2 0 3 6 0 9 (3,2) 3 0 4 0 8 0 (4,2) 4 0 0 7 0 10 (2,3) 5 (3,3) 6 (5,3) 7 (4,4) 8 (3,5) 9 (5,5) 10

arallel Numerical Computing. Lecture 4, Mar 18, 2012 45/50 Case study 2: Sparse matrix-vector multiplication Compressed row storage (CRS) format Compressed row storage (CRS) format Store the nonzero elements row-wise. Three vectors are needed: float vector storing the matrix elements: val = [1 2 5 3 6 9 4 8 7 10] int vector storing the column indices of the matrix elements col_ind = [1 2 3 2 3 5 2 4 3 5] int vector pointing to the first elements of each row row_ptr = [1 2 4 7 9] Modified CRS format: The diagonal is stored separately. Remark: If A is symmetric then it suffices to store lower (or upper) triangle!

arallel Numerical Computing. Lecture 4, Mar 18, 2012 46/50 Case study 2: Sparse matrix-vector multiplication y = Ax y = Ax With the CRS format we compute y element-by-element: y(k) = row ptr(k+1) 1 i=row ptr(k) a(i) x(col ind(i)) ( ) This is a sparse dot product. One vector is stored contiguously (stride one), the other vector is scattered in memory. Notice: In ( ) we assume that row ptr(n) points to the memory location right after the last element of a, i.e. right after the last element of the last row of A. col ind(i 1 ) col ind(i 2 ) for row ptr(k) i 1 < i 2 < row ptr(k +1).

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 47/50 Case study 2: Sparse matrix-vector multiplication y = Ax y = Ax With CRS parallelization over rows (index k) is straightforward #pragma omp parallel for private(i) for (k=0; k<m; k++){ y[k] = 0; for (i=row_ptr[k]; i<row_ptr[k+1]; i++) y[k] += val[i]*x[col_ind[i]]; } What about load balance?

arallel Numerical Computing. Lecture 4, Mar 18, 2012 48/50 Case study 2: Sparse matrix-vector multiplication y = A T x y = A T x If we multiply with the transpose of A, then we apply a sequence of sparse axpy operation: y = 0; for i = 1,..., n y = y + (i-th column of A) x i endfor In C this becomes (A T is N M). for (i=0; i<n; i++) for (k=row_ptr[i]; k<row_ptr[i+1]; k++) y[col_ind[k]] += val[k]*x[i];

Parallel Numerical Computing. Lecture 4, Mar 18, 2012 49/50 Case study 2: Sparse matrix-vector multiplication y = A T x for (i=0; i<n; i++) #pragma omp parallel for for (k=row_ptr[i]; k<row_ptr[i+1]; k++) y[col_ind[k]] += val[k]*x[i]; Typically too short loops. Notice that we know that col ind[k1] col ind[k2] in the k-loop.

arallel Numerical Computing. Lecture 4, Mar 18, 2012 50/50 Case study 2: Sparse matrix-vector multiplication y = A T x #pragma omp parallel private(j,z) { #pragma omp for for (j=0; j<n; j++) z[j] = 0.0; #pragma omp for for (i=0; i<m; i++) for (k=row_ptr[i]; k<row_ptr[i+1]; k++) z[col_ind[k]] += val[k]*x[i]; #pragma omp critical for (j=0; j<n; j++) y[j] = y[j] + z[j]; } Does not take into account that the vectors z are sparse.