From 7037335bff215496930b0698a9e3661d51a1b522 Mon Sep 17 00:00:00 2001 From: Anthony Scemama Date: Sat, 20 Nov 2021 23:19:20 +0100 Subject: [PATCH] Starting MPI --- parallelism_scemama.org | 377 +++++++++++++++++++++++++++++++++++++++- socket.png | Bin 0 -> 28969 bytes 2 files changed, 372 insertions(+), 5 deletions(-) create mode 100644 socket.png diff --git a/parallelism_scemama.org b/parallelism_scemama.org index dcf03a7..a559ed2 100644 --- a/parallelism_scemama.org +++ b/parallelism_scemama.org @@ -17,6 +17,7 @@ #+LaTeX_HEADER: \definecolor{darkblue}{rgb}{0.,0.2,0.7} #+LaTeX_HEADER: \definecolor{darkred}{rgb}{0.6,0.1,0.1} #+LaTeX_HEADER: \definecolor{darkpink}{rgb}{0.7,0.0,0.7} +#+PROPERTY: header-args :exports code #+EXPORT_EXCLUDE_TAGS: noexport #+startup: beamer @@ -645,7 +646,7 @@ user 0m3.359s sys 0m3.172s #+end_src -* Inter-process communication +* Inter-process communication :noexport: ** Processes /vs/ threads @@ -862,9 +863,10 @@ if __name__ == "__main__": #+ATTR_LATEX: :height 0.6\textheight [[./pi_convergence.png]] -** Computation of $\pi$ with pipes in Python +** Computation of \pi with pipes in Python #+begin_src python :tangle Codes/pi_python.py +#!/usr/bin/env python import os, sys from random import random, seed from math import sqrt @@ -885,7 +887,7 @@ def compute_pi(): return 4.* float(result)/float(NMAX) # Estimate of pi #+end_src -** Computation of $\pi$ with pipes in Python +** Computation of \pi with pipes in Python #+begin_src python :tangle Codes/pi_python.py def main(): @@ -911,7 +913,7 @@ def main(): sys.exit(0) #+end_src -** Computation of $\pi$ with pipes in Python +** Computation of \pi with pipes in Python #+begin_src python :tangle Codes/pi_python.py data = [] @@ -926,7 +928,7 @@ def main(): else: variance = 0. error = sqrt(variance)/sqrt(N) # Compute error - print(f"%{average} +/- %{error} %{N}") + print("%f +/- %f %d"%(average, error, N)) if N > 2 and error < error_threshold: # Stopping condition for i in range(NPROC): # Kill all children @@ -937,8 +939,372 @@ def main(): if __name__ == '__main__': main() #+end_src +** Computation of \pi with pipes in Python + + #+begin_src text +$ python pi_python.py +3.141974 +/- 0.000000 1 +3.142569 +/- 0.000000 2 +3.142168 +/- 0.000528 3 +3.141938 +/- 0.000439 4 +3.141947 +/- 0.000340 5 +[...] +3.141625 +/- 0.000107 33 +3.141614 +/- 0.000104 34 +3.141617 +/- 0.000101 35 +3.141606 +/- 0.000099 36 + #+end_src + +** Sockets + + Sockets are analogous to pipes, but they allow both ends of the pipe to be + on different machines connected by a network interface. + An Internet socket is characterized by a unique combination of: + + - A transport protocol: TCP, UDP, raw IP, ... + - A local socket address: Local IP address and port number, for example 192.168.2.2:22 + - A remote socket address: Optional (TCP) + +** Sockets + + #+ATTR_LATEX: :height 0.9\textheight + [[./socket.png]] + +** Sockets: server code in Python + + #+begin_src python :tangle Codes/socket_server.py +#!/usr/bin/env python + +import sys, os, socket, datetime +now = datetime.datetime.now + +def main(): + HOSTNAME = socket.gethostname() + PORT = 11279 + print(now(), "I am the server : %s:%d"%(HOSTNAME,PORT)) + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create an INET socket + s.bind( (HOSTNAME, PORT) ) # Bind the socket to the address and port + s.listen(5) # Wait for incoming connections + conn, addr = s.accept() # Accept connection + print(now(), "Connected by", addr) + + #+end_src + +** Sockets: server code in Python + + #+begin_src python :tangle Codes/socket_server.py + data = "" + while True: # Buffered read of the socket + message = conn.recv(8).decode('utf-8') + print(now(), "Buffer : "+message) + data += message + if len(message) < 8: break + print(now(), "Received data : ", data) + + print(now(), "Sending thank you...") + conn.send("Thank you".encode()) + conn.close() + +if __name__ == "__main__": + main() + #+end_src + +** Sockets: client code in Python + + #+begin_src python :tangle Codes/socket_client.py +#!/usr/bin/env python +import sys, os, socket, datetime +now = datetime.datetime.now + +def main(): + HOSTNAME = sys.argv[1] # Get host name from command line + PORT = int(sys.argv[2]) # Get port from command line + print(now(), "The target server is : %s:%d"%(HOSTNAME,PORT)) + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create an INET socket + s.connect( (HOSTNAME, PORT) ) # Connect the socket to the address and port + # of the server + message = "Hello, world!!!!!!!" + print(now(), "Sending : "+message) + s.send(message.encode()) # Send the data + + data = s.recv(1024) # Read the reply of the server + s.close() + print(now(), 'Received: ', data.decode('utf-8')) + +if __name__ == "__main__": main() + #+end_src + +** Sockets: Execution + + #+begin_src text +$ python Codes/socket_client.py lpqdh82 11279 +2021-11-20 21:20:32.258632 The target server is : lpqdh82:11279 +2021-11-20 21:20:32.258959 Sending : Hello, world!!!!!!! +2021-11-20 21:20:32.259042 Connected by ('127.0.0.1', 36798) +2021-11-20 21:20:32.259068 Buffer : Hello, w +2021-11-20 21:20:32.259076 Buffer : orld!!!! +2021-11-20 21:20:32.259083 Buffer : !!! +2021-11-20 21:20:32.259088 Received data : Hello, world!!!!!!! +2021-11-20 21:20:32.259093 Sending thank you... +2021-11-20 21:20:32.259133 Received: Thank you + #+end_src + + Note that the client and server can be written in different languages. + +** Server for \pi with sockets in Python + + #+begin_src python :tangle Codes/pi_server_python.py +#!/usr/bin/env python +import sys, os, socket +from math import sqrt +HOSTNAME = "localhost" +PORT = 1666 +error_threshold = 1.e-4 # Stopping criterion + +def main(): + data = [] + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create an INET socket + s.bind( (HOSTNAME, PORT) ) # Bind the socket to the address and port + while True: + s.listen(5) # Wait for incoming connections + conn, addr = s.accept() # Accept connection + X = "" + while True: # Buffered read of the socket + message = conn.recv(128) + X += message.decode('utf-8') + if len(message) < 128: break + data.append( float(X) ) + N = len(data) + #+end_src + +** Server for \pi with sockets in Python + + #+begin_src python :tangle Codes/pi_server_python.py + average = sum(data)/N # Compute average + if N > 2: # Compute variance + l = [ (x-average)*(x-average) for x in data ] + variance = sum(l)/(N-1.) + else: + variance = 0. + error = sqrt(variance)/sqrt(N) # Compute error + + print('%f +/- %f'%(average,error)) + + # Stopping condition + if N > 2 and error < error_threshold: + conn.send("STOP".encode()) + break + else: + conn.send("OK".encode()) + conn.close() + +if __name__ == "__main__": + main() + #+end_src + +** Client for \pi with sockets in Python + + #+begin_src python :tangle Codes/pi_client_python.py +#!/usr/bin/env python +import os, sys, socket +from random import random, seed +from math import sqrt +HOSTNAME = "localhost" +PORT = 1666 +NMAX = 10000000 # Nb of MC steps/process +error_threshold = 1.0e-4 # Stopping criterion +NPROC=4 # Use 4 processes + +def compute_pi(): + """Local Monte Carlo calculation of pi""" + seed(None) # Initialize random number generator + + result = 0. + for i in range(NMAX): # Loop NMAX times + x,y = random(), random() # Draw 2 random numbers x and y + if x*x + y*y <= 1.: # Check if (x,y) is in the circle + result += 1 + return 4.* float(result)/float(NMAX) # Estimate of pi + #+end_src + +** Client for \pi with sockets in Python + + #+begin_src python :tangle Codes/pi_client_python.py +def main(): + + while True: + X = compute_pi() + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Create an INET socket + try: # Connect the socket to the address and port of the server + s.connect( (HOSTNAME, PORT) ) + except socket.error: + break + message = str(X) + s.send(message.encode()) # Send the data + reply = s.recv(128).decode('utf-8') # Read the reply of the server + s.close() + + if reply == "STOP": break + +if __name__ == '__main__': + main() + + #+end_src + +** Execution of \pi with sockets in Python + + #+begin_src text +$ python pi_server_python.py & +> for i in {1..8} ; do +> python pi_client_python.py & +> done ; wait + +3.142136 +/- 0.000000 +3.141783 +/- 0.000000 +3.141992 +/- 0.000291 +3.141804 +/- 0.000279 +[...] +3.141687 +/- 0.000104 +3.141666 +/- 0.000102 +3.141651 +/- 0.000098 + + #+end_src + + * Message Passing Interface (MPI) +** Message Passing Interface + + - Application Programming Interface for inter-process communication + - Takes advantage of HPC hardware: + - TCP/IP: 50 $\mu \text{s}$ latency + - Remote Direct Memory Access (RDMA): <2 $\mu \text{s}$ + (low-latency network) + - Portable + - Each vendor has its own implementation adapted to the hardware + - Standard in HPC + - Initially designed for fixed number of processes: + - No problem for the discovery of peers + - Fast collective communications + - Single Program Multiple Data (SPMD) paradigm + +** Communicators + + - Group of processes that can communicate together + - Each process has an ID in the communicator: no need for IP + adresses and port numbers + - ~MPI_COMM_WORLD~: Global communicator, default + - ~size~: number of processes in the communicator + - ~rank~: ID of the process in the communicator + +** Point-to-point communication + +*** Python + - Send: + ~comm.send(data, dest, tag)~ + - Receive: + ~comm.recv(source, tag)~ +*** Fortran + - Send: + ~MPI_SEND(buffer, count, datatype, destination, tag, communicator, ierror)~ + - Receive: + ~MPI_RECV(buffer, count, datatype, source, tag, communicator, + status, ierror)~ +** Point-to-point communication (Python) + + #+begin_src python :tangle Codes/mpi_rank.py +from mpi4py import MPI + +def main(): + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + + if rank == 0: + data = 42 + print("Before: Rank: %d Size: %d Data: %d"%(rank, size, data)) + comm.send(data, dest=1, tag=11) + print("After : Rank: %d Size: %d Data: %d"%(rank, size, data)) + elif rank == 1: + data = 0 + print("Before: Rank: %d Size: %d Data: %d"%(rank, size, data)) + data = comm.recv(source=0, tag=11) + print("After : Rank: %d Size: %d Data: %d"%(rank, size, data)) + +if __name__ == "__main__": main() + #+end_src + +** Point-to-point communication (Python) + + #+begin_src text +$ mpiexec -n 4 python mpi_rank.py +Before: Rank: 0 Size: 4 Data: 42 +Before: Rank: 1 Size: 4 Data: 0 +After : Rank: 0 Size: 4 Data: 42 +After : Rank: 1 Size: 4 Data: 42 + #+end_src + + In Fortran, compile using =mpif90= and execute using =mpiexec= (or =mpirun=). + +** Point-to-point communication (Fortran) + + #+begin_src fortran :tangle Codes/mpi_rank.f90 +program test_rank + use mpi + implicit none + integer :: rank, size, data, ierr, status(mpi_status_size) + + call MPI_INIT(ierr) ! Initialize library (required) + if (ierr /= MPI_SUCCESS) then + call MPI_ABORT(MPI_COMM_WORLD, 1, ierr) + end if + + call MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr) + if (ierr /= MPI_SUCCESS) then + call MPI_ABORT(MPI_COMM_WORLD, 2, ierr) + end if + + call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr) + if (ierr /= MPI_SUCCESS) then + call MPI_ABORT(MPI_COMM_WORLD, 3, ierr) + end if + #+end_src + +** Point-to-point communication (Fortran) + + #+begin_src fortran :tangle Codes/mpi_rank.f90 + if (rank == 0) then + data = 42 + print *, "Before: Rank:", rank, "Size:", size, "Data: ", data + call MPI_SEND(data, 1, MPI_INTEGER, 1, 11, MPI_COMM_WORLD, ierr) + print *, "After : Rank:", rank, "Size:", size, "Data: ", data + + else if (rank == 1) then + data = 0 + print *, "Before: Rank:", rank, "Size:", size, "Data: ", data + call MPI_RECV(data, 1, MPI_INTEGER, 0, 11, MPI_COMM_WORLD, & + status, ierr) + print *, "After : Rank:", rank, "Size:", size, "Data: ", data + + end if + call MPI_FINALIZE(ierr) ! De-initialize library (required) +end program + #+end_src + +** Collective communications + +*** One-to-all + - Broadcast: send same data to all + - Scatter: distribute an array +*** All-to-one + - Reduction: Sum/product/... of data coming from all ranks + - Gather: collect a distributed array +*** All-to-all + - Reduction and broadcast + +** Deadlocks * OpenMP * Exercises @@ -1246,3 +1612,4 @@ digraph G { #+RESULTS: : /home/scemama/MEGA/TEX/Cours/TCCM/TCCM2022/Parallelism/parallelism_scemama.pdf + diff --git a/socket.png b/socket.png new file mode 100644 index 0000000000000000000000000000000000000000..ea26cc3870af1163e043ff6422da493df8a6b408 GIT binary patch literal 28969 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 literal 0 HcmV?d00001