Notas de la clase (presentación) en formato PDF

Anuncio
Elementos básicos de
cómputo paralelo
Grandes paradigmas
Máquinas de memoria compartida
Todos los procesadores ven toda la memoria al mismo tiempo.
La filosofía es básicamente dividir las tareas.
EL estándar es OpenMP (Open Multi-Processing)
Máquinas de memoria distribuida
Cada procesador tiene su propia memoria.
La filosofía es básicamente la de repartir los datos.
Es necesario la comunicación explícita para pasar datos entre
procesadores.
EL estándar es MPI (Message Passing Interface).
Procesamiento con GPUs
Posible gracias al gran número de procesadores “tontos” en las tarjetas
gráficas dedicadas.
Aún no alcanzan una madurez los compiladores y demás herramientas de
programación, pero hacia allá vamos.
Limitante más grande es la RAM de las tarjetas y la necesidad de copiar
memoria del CPU al GPU y viceversa.
Ejemplo de código con OpenMP
•Se compila el código activando las directivas de OpenMP,
e.g.
gfortran -openmp codigo.f -o ejecutable
•El numero de hebras (CPUs) se define mediante una
variable global.
!$OMP PARALLEL private(NV)
DO IP=1,NP
DO NL=1,NLEVS
!$OMP DO
DO NV=0,NVEC
LP(IP,NL,NV)=0
END DO
!$OMP END DO NOWAIT
END DO
END DO
DO NL=1,NLEVS
!$OMP DO
DO NV=0,NVEC
UP(NEQ,NL,NV)=0.
U(NEQ,NL,NV)=0.
UP(NEQ1,NL,NV)=0.
U(NEQ1,NL,NV)=0.
END DO
!$OMP END DO NOWAIT
END DO
DO NL=1,NLEVS
NVMAX(NL)=0
ISTEP(NL)=0
END DO
!$OMP END PARALLEL
MPI: Message Passing Interface
■
■
■
■
Es una librería de paso de mensajes. Disponible para FORTRAN77, C,
Fortran90 y C++ y algunos otros lenguajes de programación.
MPI sirve para comunicar entre procesos que tienen espacios de
memoria distribuidos. Nos da herramientas de:
Sincronización.
Paso de datos entre procesadores.
Documentación en:
http:/www-unix.mcs.anl.gov/mpi/
El estándar completo
http://www.mpi-forum.org
Un buen tutorial
https://computing.llnl.gov/tutorials/mpi/
Actualmente hay el estándar 3.1, pero la mayoría de lo que veremos
aquí, es de MPI Standard 1.1.
Es el standard de facto en clusters hoy en día.
Como compilar y ejecutar en MPI
En el programa necesario incluir un ‘header file’ con las definiciones de las librerías
de MPI:
include ‘mpif.h’
#include “mpi.h”
(FORTRAN)
(C)
Se usan las instrucciones mpicc, mpicxx, mpif77, mpif90, como compiladores para
C, C++, F77 y f90.
mpif77 hello.f
[opciones e.g. -O3 …] -o hello
Para ejecutarlo se usa “mpiexec”
mpiexec -np N ./hello
Donde N (entero) es el numero de procesadores
(ejemplo corriendo en 4 procesadores: mpiexec -np 4 ./hello)
Ejemplo sencillo (fortran), hello.f90
program hello
include 'mpif.h'
integer :: err, rank,size
call mpi_init(err)
call mpi_comm_rank(mpi_comm_world, rank,err)
call mpi_comm_size (mpi_comm_world, size,err)
print*,"hellp world from processor", rank, " of" , size
call mpi_finalize(err)
end program hello
Mismo ejemplo (en C), hello.c
#include <stdio.h>
#include "mpi.h"
int main( int argc, char *argv[] )
{
int rank;
int size;
MPI_Init( 0, 0 );
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("Hello world from processor %d of %d\n", rank, size);
MPI_Finalize();
return 0;
}
Instrucciones básicas de comunicación: send
■
Definición:
En C: int MPI_Send(void* buf, int count, MPI_Datatype
datatype,int dest, int tag, MPI_Comm comm)
En fortran
MPI_SEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERROR)
Instrucciones básicas de comunicación: recv
■
Definición
■
En C
int MPI_Recv(void* buf, int count, MPI_Datatype
datatype, int source, int tag, MPI_Comm comm,
MPI_Status *status)
■
En fortran
MPI_RECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS,
IERROR)
Ejemplo de uso en sendrcv.f90
Instrucción básica de sincronización
■
MPI_BARRIER
Pone un semáforo, todos los procesadores asociados al
comunicador esperan en un punto del programa, cuando
todos los lleguen ahí se reanuda la ejecución.
Una barrera está implícita en muchas de las rutinas, por
ejemoplo en las de sincronización
sintaxis:
fortran
CALL MPI_BARRIER(COMM, IERR)
C
mpi_barrier(comm)
Instrucciones colectivas
Las mas útiles son
Broadcast:
mpi_bcast
Gather:
mpi_gather
Reduce:
mpi_reduce
mpi_reduce
■
Ejemplo: sum.f90
Operaciones Predefinidas
MPI_MAX
MPI_MIN
MPI_SUM
MPI_PROD
MPI_LAND
MPI_BAND
MPI_LOR
MPI_BOR
MPI_LXOR
MPI_BXOR
MPI_MAXLOC
MPI_MINLOC
maximum
minimum
sum
product
logical and
bit-wise and
logical or
bit-wise or
logical xor
bit-wise xor
max value and location
min value and location
Filosofía para códigos eulerianos
■
Se parte el dominio y cada
procesador resuelve un
pedazo
La comunicación se convierte
en el paso de condiciones de
frontera.
Condiciones de frontera y “celdas fantasma”
nx=nxtot/(num of proc en x)
0, 1…
…nx,nx+1
0, 1…
0, 1…
…nxtot,nxtot+1
0, 1…
…nx,nx+1
En código (f90)
…
integer, parameter :: neq=4
integer, parameter :: nghost=1
integer, parameter :: nxtot=512,nytot=512
! Numero de ecuaciones
! Celdas fantasma
! Tamaño de la malla
!arreglo de procesadores en las direcciones x e y
integer, parameter :: mpiNX=2, mpiNY=2
!tamaño de los arreglos para c/processador
integer, parameter :: nx=nxtot/mpiNX, ny=nytot/mpiNY
!limites de los arreglos
integer, parameter :: nxmin=1-nghost, nxmax=nx+nghost
integer, parameter :: nymin=1-nghost, nymax=ny+nghost
!definimos los arreglos
real :: u(neq,nxmin:nxmax,nymin:nymax)
…
Creando un arreglo cartesiano de procesadores
!procs en X (columnas mpiNX)
dims(1) = 4
!procs en y (filas, mpiNY)
dims(2) = 3
!periodicidad en x?
periods(1) = .true.
Periodicidad en y?
periods(2) = .false.
!reordena los procesadores?
reorder
= .true.
!dimensiones
ndim
= 2
call MPI_CART_CREATE( MPI_COMM_WORLD, ndim, dims,
$
periods, reorder, comm2d, ierr )
Identificandose dentro del arreglo
program main
Include ‘mpif.h’
integer, parameter :: ndim=2
!Dimensiones
integer, parameter :: mpiNX=2, mpiNY=2
!procs en x e y
logical, parameter :: reorder=.true.
integer, dimension(0:ndim-1) :: dims, coords
logical, dimension(0:ndim-1):: period
! Variables adicionales
integer :: rank, nps, ireq(2), err, comm2d, me, status(MPI_STATUS_SIZE)
period(0) = .false.
dims(0)
= mpiNX
; period(1) = .false.
; dims(1)
= mpiNY
call mpi_init (err)
call mpi_comm_rank (mpi_comm_world,rank,err)
call mpi_comm_size (mpi_comm_world,nps,err)
!inicializa mpi
!obtiene su ID
!obtiene el num total de procs
if(rank == 0) then
print * ,'*****************************************'
print '(a,i3,a)',' * running with mpi with', nps , ' processors *'
print * ,'*****************************************'
end if
!Crea arreglo de procesadores
call mpi_cart_create(mpi_comm_world, ndim, dims, period, reorder, comm2d, err)
call mpi_comm_rank(comm2d, rank, err)
!actualiza su ID
call mpi_cart_coords(comm2d, rank, ndim, coords, err)
!obtiene coordenadas
print '(a,i3,a,2i4)', 'processor ', rank,' ready w/coords',coords(0),coords(1)
call mpi_finalize(err)
end program main
Para ubicar las coordenadas x,y,(z), en cada procesador
(e.g. para poner condiciones iniciales.)
■
En fortran:
dx=xphys/nxtot
dy=xphys/nytot
x=( float(i+coords(0)*nx) ) * dx
y=( float(j+coords(1)*ny) ) * dy
r=sqrt(x**2.+y**2.)
Identificando a los “vecinos”
MPI_CART_SHIFT(COMM, DIRECTION, DISP, RANK_SOURCE, RANK_DEST,
IERROR)
NTEGER COMM, DIRECTION, DISP, RANK_SOURCE, RANK_DEST,
IERROR)
■
Agregar lo siguiente al programa anterior
integer :: left, right, bottom, top
call mpi_cart_shift(comm2d, 0, 1, left , right, ierr)
call mpi_cart_shift(comm2d, 1, 1, bottom, top , ierr)
print*,rank,left,right,top,bottom
Pasando las c. de frontera
■
■
Es conveniente usar la rutina mpi_sendrecv, una vez
habiendo identificado los vecinos.
definición:
MPI_SENDRECV(SENDBUF, SENDCOUNT, SENDTYPE, DEST, SENDTAG,
RECVBUF, RECVCOUNT, RECVTYPE, SOURCE, RECVTAG,
COMM, STATUS, IERROR)
Ejemplo c. de frontera
!definimos arreglos para pasar las condiciones de frontera
real, dimension(neq,nxmin:nxmax,1) :: bxsendt,bxrecvt,bxsendb,bxrecvb
real, dimension(neq,1,nymin:nymax) :: bysendr,byrecvr,bysendl,byrecvl
!cuantos elementos en cada frontera (suponiendo una sola celda fantasma)
integer, parameter :: bxsize=neq*(nxmax-nxmin)
integer, parameter :: bysize=neq*(nymax-nymin)
!llenamos arreglos que se van a pasar
bysendr(:,1,:)=u(:,nx-1, : )
bysendl(:,1,:)=u(:, 1 , : )
bxsendt(:,:,1)=u(:, : ,ny-1)
bxsendb(:,:,1)=u(:, : , 1 )
!se mandan y reciben los datos
call mpi_sendrecv(bysendr, bysize,
byrecvl, bysize,
call mpi_sendrecv(bxsendt, bxsize,
bxrecvb, bxsize,
call mpi_sendrecv(bysendl, bysize,
byrecvr, bysize,
call mpi_sendrecv(bxsendb, bxsize,
bxrecvt, bxsize,
!se reacomodan
if (left .ne. -1) u(:, 0 , : )
if (right .ne. -1 u(:,nx+1, : )
if (bottom.ne. -1) u(:, : , 0 )
if (top
.ne. -1) u(:, : ,ny+1)
mpi_real,
mpi_real,
mpi_real,
mpi_real,
mpi_real,
mpi_real,
mpi_real,
mpi_real,
=
=
=
=
right ,
left ,
top
,
bottom,
left ,
right ,
bottom,
top
,
byrecvl(:,1,:)
byrecvr(:,1,:)
bxrecvb(:,:,1)
bxrecvt(:,:,1)
0,
0,
0,
0,
0,
0,
0,
0,
&
comm2d, status , err)
&
comm2d, status , err)
&
comm2d, status , err)
&
comm2d, status , err)
Descargar