OpenMP - dcce.ibilce.unesp.braleardo/cursos/hpc/openMP2020.… · Programa OpenMP começa com um...
Transcript of OpenMP - dcce.ibilce.unesp.braleardo/cursos/hpc/openMP2020.… · Programa OpenMP começa com um...
OpenMP
Aleardo Manacero Jr.
Copyright Notice
Material parcialmente retirado de“Introduction to OpenMP”, por Rudi Eigenmann -- [email protected], Bob Kuhn -- [email protected], Tim Mattson -- [email protected], e Ramesh Menon -- [email protected]
Copyright © 1997-99 OpenMP Architecture Review Board.
E também de “OpenMP”, por Blaise Barney – Lawrence Livermore Laboratory, https://computing.llnl.gov/tutorials/openMP/
Introdução
API portável para sistemas multiprocessadores de memória compartilhada
Fortran 77, Fortran 90, C, C++ Suporte para UNIX e NT
Padroniza paralelismo em grão fino (laços)
Suporta algoritmos de grão grosso
É baseada em diretivas de compilação
Introdução
Não é uma nova linguagem
São diretivas, bibliotecas e variáveis de ambientes extendendo uma linguagem básica: f77, f90, C, C++
Não faz paralelização automática
Usuário explicitamente especifica as regiões de execução paralela
Compilador faz a paralelização dessas regiões
ArquiteturaCódigo fonte (C, p.ex.), com diretivas openMP
Modelo de uso em C
Threads com índices locais e vetor compartilhado entre threads
main() {
#pragma omp parallel for \
shared(A)private(i)
for( i=1; i <=100; i++ ) {
. . .
}
}
Modelo de paralelização
Programa OpenMP começa com um thread sequencial
Para criar novos threads o usuário define uma região paralela
Threads trabalhadores são disparados
Mestre é parte dos threads trabalhadores
Threads saem ao final da região paralela (dormem)
Modelo Fork-Join
Visão geral de construções OpenMP
Binding Nesting
Conditional Compilation
If()
Parallel Region
Schedule()
Ordered
Do
Sections
Single
Work Sharing
Control Constructs
ThreadPrivate
Shared()
FirstPrivate()
LastPrivate()
Private()
Reduction(:)
CopyIn()
Default()
Data Scope
Data Constructs
Master
Critical
Barrier
Atomic
Ordered
Synchronization Constructs
Directives
Environment Functions
Lock Functions
Runtime Library
Static
Dynamic,chunk
Guided,chunk
OMP_SCHEDULE
OMP_NUM_THREADS
OMP_DYNAMIC
OMP_NESTED
Environment Variables
OpenMP
Criando threads em OpenMP
Apenas uma forma de criar threads em OpenMP
FortranC$OMP PARALLEL Code to be executed by each threadC$OMP END PARALLEL
C#pragma omp parallel { Code to be executed by each thread}
Criando threads em OpenMP
A diretiva parallel pode receber cláusulas de execução
Essas cláusulas podem definir número de threads ou que variáveis serão compartilhadas entre threads
#pragma omp parallel num_threads ( that_many ){ Code to be executed by each thread}
#pragma omp parallel shared(A) private(i){ Code to be executed by each thread}
Variáveis nos threads
Variáveis podem ser passadas aos threads de vários modos
Como openMP funciona em memória compartilhada, o modo padrão é que sejam compartilhadas entre threads
Fora isso podem ser privativas (private) a cada thread
Ou terem valor inicial comum (firstprivate) ou valor final “comum” (lastprivate)
Exclusão Mútua
Pode ser implementada de duas formas:
Região crítica
Execução atômica
Região críticaPodem ser nomeadas e nomes são globais
Não podem conflitar com nomes de funções e outras entidades globais
#pragma omp parallel { …… #pragma omp critical(left) A[i] += Alocal ……}
Exclusão Mútua
Execução atômicaNão são estruturáveis
É aplicada apenas ao comando seguinte
Otimizam a exclusão mútua se o hardware permitir
#pragma omp parallel { …… #pragma omp atomic A[i] += Alocal ……}
Exclusão Mútua
Execução sequencialÉ utilizada para o controle de laços de repetição
Além de garantir a exclusão mútua obriga também a execução sequencial do laço
#pragma omp parallel { …… #pragma omp ordered printf(“%d\n”, i); ……}
Exclusão Mútua
Barreira de sincronismo
Diretiva BARRIER
Threads esperam chegada de todos
Barreira implícita ao final de cada região paralela
#pragma omp parallel { ……
#pragma omp barrier ……}
Laços de repetição
Diretiva DO/for
Deve estar dentro de um trecho paralelo
Divide as iterações entre os threads
#pragma omp parallel shared(a,b,c,chunk)private(i) {
#pragma omp for schedule(dynamic,chunk) nowait
for (i=0; i < N; i++)
c[i] = a[i] + b[i];
} /* end of parallel region */
Atribuindo trabalho aos threads
Atributo schedule
Pode ser estático, dinâmico, guiado ou runtime
#pragma omp parallel shared(a,b,c,chunk)private(i) {
#pragma omp for schedule(static, chunk) nowait
for (i=0; i < N; i++)
c[i] = a[i] + b[i];
} /* end of parallel region */
Atribuindo trabalho aos threads
Escalonamento estático
Atribuindo trabalho aos threads
Escalonamento dinâmico e guiado
Seções
Diretiva SECTION
Atribui tarefas distintas aos threads
#pragma omp sections nowait {
#pragma omp section
do_a_section
#pragma omp section
do_another_section
} /* end of sections region */
Seção do thread mestre
Diretiva MASTER
Trecho executado apenas pelo thread principal
Demais threads continuam a execução depois da seção master
#pragma omp parallel { ……
#pragma omp master printf(“Hello World”);
……}
Seção de thread único
Diretiva SINGLE
Trecho executado apenas por um dos threads Demais threads continuam a execução depois da seção single
#pragma omp parallel { ……
#pragma omp single printf(“Hello World”);
……}
Um exemplo
Hello World paralelo
Dispara um conjunto de threads, sendo que cada um imprime o seu ID
O thread principal imprime o total de threads disparados
Um exemplo
#include <omp.h>#include <stdio.h>#include <stdlib.h>
int main (int argc, char *argv[]) {int nthreads, tid;
/* Dispara threads com suas cópias das variáveis */#pragma omp parallel private (nthreads, tid) { /* Descobre seu ID */ tid = omp_get_thread_num(); printf("Hello World from thread = %d\n", tid);
Um exemplo
/* Só o thread principal (mestre) faz isso */ if (tid == 0) { nthreads = omp_get_num_threads(); printf("Number of threads = %d\n", nthreads); }
} /* Todos os threads se juntam ao thread principal e “somem” */
}
Um exemplo
Para compilar esse código o comando é
gcc -o hello arquivo.c -fopenmp
Antes de executar é preciso definir o número de threads, 8 nesse exemplo, com
export OMP_NUM_THREADS=8
Um exemplo
Exemplo tirado de “An introduction to Parallel Programming”, P. Pacheco
Faz a integração de uma função através do método do trapézio
Usa “omp critical” para garantir a exclusão mútua no processo de acumulação na variável “global_result_p”
Um exemplo
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
void Trap(double a, double b, int n, double* global_result_p);
int main(int argc, char* argv[ ]) {
double global_result = 0.0;
double a, b;
int n;
int thread_count;
Um exemplo
thread_count = strtol(argv[1], NULL, 10);
printf("Enter a, b, and n \n");
scanf("%lf %lf %d", &a, &b, &n);
# pragma omp parallel num_threads(thread_count)
Trap(a, b, n, &global_result);
printf("With n = %d trapezoids, our estimate \n", n);
printf("of the integral from %f to %f = %.14e \n", a, b, global_result);
return 0;
} /* main */
Um exemplo
void Trap(double a, double b, int n, double* global_result_p) {
double h, x, my_result;
double local_a, local_b;
int i, local_n;
int my_rank = omp_get_thread_num();
int thread_count = omp_get_num_threads();
h = (b-a)/n; local_n = n/thread_count;
local_a = a + my_rank*local_n*h; local_b = local_a + local_n*h;
my_result = (f(local_a) + f(local_b))/2.0;
Um exemplo
for (i = 1; i <= local n-1; i++) {
x = local_a + i * h;
my_result += f(x);
}
my_result = my_result * h;
# pragma omp critical
*global_result_p += my_result;
} /* Trap */
// função f( ) deve ser definida pelo usuário
Mais um exemplo
Um exemplo que será explorado outras vezes é o problema de transferência de calor, enunciado da seguinte forma:
Suponha uma chapa em que as bordas são mantidas em temperatura constante, assim como pontos isolados da chapa. Como determinar a temperatura em um ponto qualquer da chapa?
Transferência de calor
Pode-se usar algoritmos de aproximações sucessivas, como o chamado algoritmo red & black
Nesse caso, cada ponto da chapa tem sua temperatura, numa iteração, como sendo a média das temperaturas de seus vizinhos e de sua própria na iteração anterior
Transferência de calor
Os pontos a serem considerados como vizinhos em cada iteração são os vizinhos na vertical e na horizontal, como em:
Transferência de calor
Assim, a chapa pode ser vista como uma enorme matriz, em que cada elemento é o valor da temperatura da chapa num ponto
Os valores de contorno e constantes são também considerados elementos da matriz
Transferência de calor
O algoritmo Red & Black faz o cálculo das temperaturas numa iteração considerando valores da matriz red, armazenando os novos resultados na matriz black
Na iteração seguinte a matriz red passa a ser o destino dos resultados calculados a partir da matriz black (daí o nome do algoritmo!)
Transferência de calor
A programação desse algoritmo em paralelo usando openMP é feita disparando uma certa quantidade de threads, que devem ser sincronizados a cada iteração
O procedimento é simples, demandando
Exemplo de chapa
Mais um exemplo
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>
#define cols 200
#define totrows 200
#define maxtime 180000
Mais um exemplo
int main(int argc, char* argv[ ])
{double red[totrows+2][cols+2], black[totrows+2][cols+2];
int r, c, tick;
int thread_count;
thread_count = strtol(argv[1], NULL, 10);
Mais um exemplo
for (tick = 1; tick <= maxtime; tick++) {
// Inicia valores das fontes constantes de calor
black[totrows/3][cols/3] = 10.0;
black[2*totrows/3][cols/3] = 20.0;
black[totrows/3][2*cols/3] = -20.0;
black[2*totrows/3][2*cols/3] = 20.0;
Mais um exemplo
/* Calcula-se as temperaturas nessa iteração */ #pragma omp parallel shared (black,red,thread_count) \ private(r,c) { #pragma omp for schedule (dynamic, totrows/thread_count) for (r=1; r <= totrows; r++) for (c=1; c <= cols; c++) red[r][c] = ( black[r][c] + black[r][c-1] + black[r-1][c] + black[r+1][c] + black[r][c+1] ) / 5.0; }
Mais um exemplo
// Red part of the algorithm
red[totrows/3][cols/3] = 10.0;
red[2*totrows/3][cols/3] = 20.0;
red[totrows/3][2*cols/3] = -20.0;
red[2*totrows/3][2*cols/3] = 20.0;
Mais um exemplo
/* Calcula-se as temperaturas nessa iteração */ #pragma omp parallel shared (black,red,thread_count) \ private(r,c) { #pragma omp for schedule (dynamic, totrows/thread_count) for (r=1; r < totrows; r++) for (c=1; c < cols; c++) black[r][c] = ( red[r][c] + red[r][c-1] + red[r-1][c] + red[r+1][c] + red[r][c+1] ) / 5.0; } } /* Fim do laço “for (tick = 1; tick <= maxtime; tick++)” */
Mais um exemplo
// Reatribuindo valores constantes black[totrows/3][cols/3] = 10.0; black[2*totrows/3][cols/3] = 20.0; black[totrows/3][2*cols/3] = -20.0; black[2*totrows/3][2*cols/3] = 20.0;
for (r=62; r <= 72; r++) { for (c=62; c <= 70; c++) printf("%lf ",black[r][c]); puts(" "); } return 0;} /* main */
openMP