INTRODUÇÃO AO OPENMP
PROF. ANDRÉ LEON S. GRADVOHL, DR.
[email protected]
ARQUITETURAS
PARALELAS
Arquiteturas paralelas:
• Single Instruction Multiple Data
• Máquinas Vetoriais
• Multiple Instruction Multiple Data
• Memória distribuída
• Memória compartilhada
2
Onde o OpenMP se
encaixa
MODELOS DE
PROGRAMAÇÃO
PARALELA
Modelos de Programação Paralela
• Multiprocessamento
• Fork/Join
• Passagem de Mensagens
• Exemplo: PVM, MPI
• Multithread
• Exemplo: OpenMP, POSIX-Threads
OpenMP (Open MultiProcessing):
3
• Interface de programação que suporta multiprocessamento
em ambientes de memória compartilhada.
INTRODUÇÃO AO
OPENMP
Estrutura de um programa OpenMP:
• Em Fortran:
4
PROGRAM HELLO
INTEGER VAR1, VAR2, VAR3
*** Código serial
*** Início da seção paralela. “Fork” um grupo de “threads”.
!$OMP PARALLEL PRIVATE(VAR1, VAR2) SHARED(VAR3)
*** Seção paralela executada por todas as “threads”
*** Todas as “threads” efetuam um “join” a thread mestre e
finalizam
!$OMP END PARALLEL
*** Código serial
END
INTRODUÇÃO AO
OPENMP
Estrutura de um programa OpenMP:
• Em C:
5
#include <omp.h>
int main ()
{
int var1, var2, var3;
*** Código serial
*** Início da seção paralela. “Fork” um grupo de “threads”.
#pragma omp parallel private(var1, var2) shared(var3)
{
*** Seção paralela executada por todas as “threads”
*** Todas as “threads” efetuam um “join” a thread mestre e finalizam
}
*** Código serial
}
INTRODUÇÃO AO
OPENMP
Observações
Fork/Join:
• Quando uma thread chega a uma definição de região paralela, ela
cria um conjunto de threads e passa a ser a thread mestre. A thread
mestre faz parte do conjunto de threads e possui o número de
identificação “0”.
• A partir do início da região paralela, o código é duplicado e todas as
threads executarão esse código.
• Existe um ponto de sincronização (“barreira”) no final da região
paralela, sincronizando o fim de execução de cada thread. Somente
a thread mestre continua desse ponto.
6
B
A
R
R
INTRODUÇÃO AO
OPENMP
Observações
7
• O número de threads
• Em uma execução com o OpenMP, o número de “threads” é
determinado pelos seguintes fatores, em ordem de precedência:
1. Utilização da função omp_set_num_threads() no código
Fortran ou C/C++;
2. Definindo a variável de ambiente OMP_NUM_THREADS,
antes da execução;
3. Implementação padrão do ambiente: número de processadores
em um nó.
• Restrições
• Não é permitido caminhar para dentro ou fora (”branch”) de uma
estrutura de blocos definida por uma diretiva OpenMP e somente um
IF é permitido.
EXEMPLO
#include <stdio.h>
#include <omp.h>
Faz o fork dos threads e
mantém suas próprias cópias
de variáveis.
Obtém
o número
do Thread.
Região
Paralela
int main ()
Se tid == 0, então é o thread
{ int nthreads, tid;
mestre.
Obtém
#pragma omp parallel private(nthreads,
tid) a quantidade de
{
threads.
tid = omp_get_thread_num();
printf(“Ola Mundo do thread = %d\n”, tid);
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf(“Numero de threads = %d\n”, nthreads);
}
}
}
CRIAÇÃO DE
THREADS
#include <stdio.h>
#include <omp.h>
int main()
Criação de 4
threads.
{
double A[1000];
#pragma omp parallel num_threads(4)
{
Obtém o número
do thread.
int ID = omp_get_thread_num();
pooh(ID,A);
}
return 0;
9
}
CRIAÇÃO DE THREADS –
EXERCÍCIOS
Matematicamente sabe-se que:
1
4
0 (1  x 2 )  
Portanto, é possível aproximar esta integral como um somatório:
n
 F ( x )x  
i 0
i
10
Onde cada retângulo tem largura x e altura F(xi) no meio do intervalo i.
CRIAÇÃO DE THREADS –
EXERCÍCIOS
Solução Serial:
static long num_steps = 100000;
double step;
int main ()
{
int i; double x, pi, sum = 0.0;
step = 1.0/(double) num_steps;
for (i=0;i< num_steps; i++){
x = (i+0.5)*step;
sum = sum + 4.0/(1.0+x*x);
}
pi = step * sum;
11
}
CRIAÇÃO DE THREADS –
EXERCÍCIOS
12
Solução Paralela
static long num_steps = 100000000;
double step;
int main ()
{
int i,j;
double pi, full_sum = 0.0;
double sum[MAX_THREADS];
step = 1.0/(double) num_steps;
CRIAÇÃO DE THREADS –
EXERCÍCIOS
omp_set_num_threads(MAX_THREADS);
full_sum=0.0;
#pragma omp parallel
{
int i;
int id = omp_get_thread_num();
int numthreads = omp_get_num_threads();
double x;
Região Paralela
sum[id] = 0.0;
if (id == 0) printf(" num_threads = %d",numthreads);
for (i=id;i< num_steps; i+=numthreads){
x = (i+0.5)*step;
sum[id] = sum[id] + 4.0/(1.0+x*x);
}
13
}
CRIAÇÃO DE THREADS –
EXERCÍCIOS
for(full_sum = 0.0, i=0;i< MAX_THREADS;i++)
full_sum += sum[i];
pi = step * full_sum;
}
14
Agrupamento dos resultados.
(Redução)
Define uma região
paralela
DIRETIVA DO/FOR
Com isso, o programador
não precisa de preocupar
com a divisão da carga de
trabalho entre os threads
Exemplo:
#pragma omp parallel
{
#pragma omp for
for (I=0;I<N;I++)
Faca_algo(I);
}
O comando for será dividido
igualmente entre os threads.
Detalhe:a variável I é privativa.
15
A diretiva DO/for especifica
que as iterações de um laço
sejam distribuídas e
executadas em paralelo
pelo grupo de threads. A
região paralela tem que ter
sido identificada antes.
DIRETIVA DO/FOR
Cuidado com o uso de outras
variáveis dento de um “laço
paralelo”!!
As variáveis do laço são privativas
de cada thread.
Exemplo:
#pragma omp parallel for
for (I=0;I<N;I++)
Faca_algo(I);
 Exemplo:
int i, j, A[MAX];
j = 5;
for (i=0;i< MAX; i++) {
j = j+ 2*i;
A[i] = big(j);
}
16
Alternativamente, podese utilizar a seguinte
sintaxe:
DIRETIVA DO/FOR REDUÇÃO
Como resolver o caso
ao lado?
double ave=0.0, A[MAX]; int i;
for (i=0;i< MAX; i++) {
ave + = A[i];
}
ave = ave/MAX;
17
Resposta: usando uma redução !
double ave=0.0, A[MAX]; int i;
#pragma omp parallel for reduction (+:ave)
for (i=0;i< MAX; i++) {
ave + = A[i];
}
ave = ave/MAX;
DIRETIVA DO/FOR –
REDUÇÃO – MAIS
DETALHES
Dentro de uma região paralela:
18
• É feita uma cópia local de cada variável e inicializada
com um valor que depende do tipo de operação, e. g.,
na operação de + o valor inicial é zero.
• As cópias locais são reduzidas (somadas,
multiplicadas etc) em um único valor que,
posteriormente, são combinados em uma única
variável comum.
Como fica o programa para calcular o  com a diretiva
for e a redução?
DIRETIVA DO/FOR –
REDUÇÃO – MAIS
DETALHES
19
Solução Paralela
static long num_steps = 100000000;
double step;
int main ()
{
int i,j;
double x, pi, sum = 0.0;
double sum[MAX_THREADS];
step = 1.0/(double) num_steps;
DIRETIVA DO/FOR –
REDUÇÃO – MAIS
DETALHES
omp_set_num_threads(MAX_THREADS); sum=0.0;
#pragma omp parallel for reduction (+:sum)
for (i=1;i< num_steps; i++){
Região Paralela com
redução
x = (i-0.5)*step;
sum= sum + 4.0/(1.0+x*x);
}
pi = step * sum;
20
}
ATRIBUTOS DE
VARIÁVEIS
As variáveis no OpenMP podem ser
compartilhadas ou não entre os threads. Esse
controle é feito através de alguns atributos. São
eles:
21
private: Declara que as variáveis listadas serão de
uso específico de cada “thread”. Essas variáveis
não são iniciadas.
shared (default) Declara que as variáveis listadas
compartilharão o seu conteúdo com todas as
threads de um grupo. As variáveis existem em
apenas um endereço de memória, que pode ser
lido e escrito por todas as threads do grupo.
ATRIBUTOS DE
VARIÁVEIS
22
firstprivate: Define uma lista de variáveis com
o atributo PRIVATE, mas sendo inicializadas
automaticamente, de acordo com o valor que
possuíam no thread antes de uma região
paralela.
lastprivate: Define uma lista de variáveis com
o atributo PRIVATE e copia o valor da última
iteração de um laço da última thread que
finalizou.
default: Permite que o programador defina o
atributo “default” para as variáveis em uma
região paralela (PRIVATE, SHARED ou
NONE).
ATRIBUTOS DE
VARIÁVEIS EXEMPLOS
void useless() {
Cada thread possui sua própria cópia da
variável tmp com o valor inicial zero.
int tmp = 0;
#pragma omp parallel for firstprivate(tmp)
for (int j = 0; j < 1000; ++j)
tmp += j;
printf(“%d\n”, tmp);
23
}
ATRIBUTOS DE
VARIÁVEIS EXEMPLOS
void useless() {
int tmp = 0;
#pragma omp parallel for firstprivate(tmp) lastprivate(tmp)
for (int j = 0; j < 1000; ++j)
tmp += j;
}
A variável tmp termina com o valor do
calculado pelo último thread a terminar.
24
printf(“%d\n”, tmp);
SEÇÕES
1.
A diretiva sections define a seção do código sequencial onde será
definida as seções independentes, através da diretiva section;
2.
Cada section é executada por uma thread do grupo;
3.
Existe um ponto de sincronização implícita no final da diretiva
section, a menos que se especifique o atributo nowait;
4.
Se existirem mais threads do que seções, o OpenMP decidirá, quais
threads executarão os blocos de section, e quais, não executarão.
25
A diretiva sections divide o trabalho de forma não
iterativa em seções separadas, aonde cada seção
será executada por uma “thread” do grupo.
Representa a implementação de paralelismo
funcional, ou seja, por código.
Algumas observações:
SEÇÕES- EXEMPLO
#include <omp.h>
#define N 1000
Definição de uma área de seções.
int main () {
int i, n=N;
float a[N], b[N], c[N];
for (i=0; i < N; i++) a[i] = b[i] = i * 1.0;
#pragma omp parallel shared(a,b,c,n) private(i) {
#pragma omp sections nowait {
#pragma omp section
Primeira seção
for (i=0; i < n/2; i++)
c[i] = a[i] + b[i];
#pragma omp section
for (i=n/2; i < n; i++)
Segunda seção
c[i] = a[i] + b[i];
} /* fim parallel */
}
26
} /* fim seções*/
UNICIDADE
A diretiva single determina que o código
identificado seja executado por somente uma
thread do grupo.
Os threads do grupo que não executam a diretiva
single, esperam o fim do processamento da
thread que executa a diretiva, a menos que se
especifique o atributo nowait.
27
A diretiva ordered determina que as iterações do
laço na região paralela, sejam executados na
ordem sequêncial.
UNICIDADEEXEMPLOS
#pragma omp parallel
{
do_many_things();
#pragma omp single
Apenas um thread vai executar esse
trecho de código.
Detalhe: nesse caso todos os threads
aguardarão até que o bloco single seja
executado.
{
exchange_boundaries();
}
do_many_other_things();
28
}
UNICIDADEEXEMPLOS
#pragma omp parallel private (tmp)
#pragma omp for ordered reduction(+:res)
for (I=0;I<N;I++){
tmp = NEAT_STUFF(I);
#pragma ordered
res += consum(tmp);
}
29
Os threads executarão os trechos de
código um de cada vez, de forma
sequencial.
Download

Introdução ao OpenMP