Introduction to Programming with MPI ------------------------------------ Practical Exercise 03 (Datatypes and Collectives) ------------------------------------------------- The following instructions apply to the other practicals as well, but are particularly relevant to this one. Be careful to check your results against the supplied ones before proceeding (note that the order of output from the processes may vary). Each change is small, and is intended to show how something can be done, or the effect a change will have. A major point of the practicals is so that you can check your expectations of what has changed and what has not changed against what actually happens. Note that several programs are used as starting points in later questions, to minimise the amount of typing you need to do; those are flagged with an instruction to make a safe copy of them, and the name that will be used to refer to them later. If you are unable to complete the exercise successfully, you can use a specimen answer to this practical as a starting point for the later one. But it is better to use your own code. If you find that doing these exercises takes too long, you can stop after a few and complete them later. The reason that there are so many of them is to provide one example of every major feature. The term 'display' is used to mean the statement that shows the values of data - i.e. WRITE, 'cout <<' or printf, according to your language. Examples using character data will be provided later, when we come to how to do I/O properly. Question 1 ---------- This question is intended to check that you know the absolute basics of collectives, before trying harder problems. 1.1 Create a C/C++ 'double' or Fortran REAL(KIND=DP) array of length 5, and initialise it to zero. File 'Programs/reals.input' in your home directory contains some fixed-point numbers in free format; read 5 of them into an array in MPI process 1 (one), broadcast it to all processes, and display the process number and the array in each process in a message like: Process 3 now has data 1.234, 4.567, 6.789, 9.012, 2.345 Make a safe copy of this program, and call it 'Ahab'. 1.2 Change the program to read the data into process 2, not process 1, broadcast only the 3 middle elements of the array, but continue to display all of the array. 1.3 Take a copy of the program used in question 1.1, and change it to read the file 'Programs/integers.input' and use a C/C++ 'int' or Fortran INTEGER array. Question 2 ---------- This question is intended to check that you know how to work with MPI buffers. 2.1 Take a copy of the program used in question 1.1, and change it to use arrays of length 12 and read and display 12 elements. 2.2 Change the program to scatter the elements rather than broadcast them (i.e. the first 3 go to process 0, the next 3 to process 1 and so on). Remember to use separate arrays for the send and receive (and initialise both to zero), but make both of length 12, and display both arrays on separate lines. 2.3 Change the program so that process 0 receives its elements into the first 3 elements of its array (0-2 in C/C++, 1-3 in Fortran), the second into the second 3 elements, and so on. 2.4 Change the program so that the receiving arrays are of length 3, rather than 12. Make a safe copy of this program, and call it 'Ishmael'. 2.5 Change the program to use 3 processes (i.e. '-np 3') and scatter 4 elements to each. 2.6 Take a copy of the program used in question 2.4, and change it to read the file 'Programs/integers.input' and use a C/C++ 'int' or Fortran INTEGER array. Hiatus ------ The above questions cover most of the basic facilities covered before the hiatus. You should leave the subsequent questions until after the rest of the lecture. Question 3 ---------- This question is a little more complicated, and shows how you can use MPI to perform calculations in parallel. 3.1 Take a copy of the program used in question 2.4, and change it to read into a two dimensional array (double[4][3] in C/++, DIMENSION(3,4) in Fortran). Then scatter the data as before. 3.2 Change the program so that all processes take the exponential (exp) of them (after they have been scattered and before they are displayed), gather them back into the array that was used to read in the data, and display that array on each of the processes. Make a safe copy of this program, and call it 'Starbuck'. 3.3 Change the program to use Allgather, rather than Gather. Question 4 ---------- This shows how to use simple reductions. 4.1 Take a copy of the program used in question 3.2 that was called 'Starbuck', and remove the first display statement (i.e. the one that prints the initial values). Change it to declare a new array of length 3 and initialise that to zero. Then sum the elements of the gathered array, and display that array as well. Note that you need do the summation on process 1 only but, as all arrays are initialised to zero, doing it on all processes is harmless. Do whichever you prefer, or try both and see what happens. 4.2 Change the program to replace the gather and summation by a reduction. Make a safe copy of this program, and call it 'Queequeg'. 4.3 Change the program to use Allreduce, rather than Reduce. 4.4 Change the program to read the file 'Programs/integers.input', use a C/C++ 'int' or Fortran INTEGER array and remove the exponentiation. 4.5 Change the program to display the numbers in hexadecimal and use bitwise exclusive OR rather than summation. 4.6 Change the program to use Boolean (logical) arrays, initialise them to False, set them True if the integer is greater than 5000, and use Boolean AND rather than exclusive OR. Question 5 ---------- MPI all-to-all is quite easy to implement using scatter and gather, but it is both tedious and inefficient. It is well worth learning how to use MPI_Alltoall. 5.1 Take a copy of the program used in question 3.2 that was called 'Starbuck', change the main data array to a two dimensional integer array of size 4x4, and read in 16 elements from the file 'Programs/integers.input'. Remove the exponential (exp) function (i.e. more like 3.1). Declare a one dimensional integer array of size 4, scatter the input to that, and display the values received. Then declare another one dimensional integer array of size 4 (or a two-dimensional one of 1x4), use all-to-all to transfer the received results to that, and display the values transferred. Note that you need to change the vector length to 1, and notice how you have transposed the matrix across processes. Make a safe copy of this program, and call it 'Stubb'. You are recommended NOT to spend too long before asking for help if your program behaves weirdly or crashes; while this example is fairly simple, there are lots of ways to make errors. This example causes more problems than almost any other. 5.2 Change the program to make two dimensional array to 3x4x4 (int[4][4][3] in C/C++, DIMENSION(3,4,4) in Fortran), read in 48 elements, change the one dimensional ones to 3x4, and use all-to-all to transpose the data in blocks of 3 integers. Display the effects as before.