290 likes | 383 Views
Introdução ao OpenMP. Prof. André Leon S. Gradvohl , Dr . gradvohl@ft.unicamp.br. Arquiteturas paralelas. Arquiteturas paralelas: Single Instruction Multiple Data Máquinas Vetoriais Multiple Instruction Multiple Data Memória distribuída Memória compartilhada.
E N D
Introdução ao OpenMP Prof. André Leon S. Gradvohl, Dr. gradvohl@ft.unicamp.br
Arquiteturas paralelas • Arquiteturas paralelas: • Single Instruction Multiple Data • Máquinas Vetoriais • Multiple Instruction Multiple Data • Memória distribuída • Memória compartilhada 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): • Interface de programação que suporta multiprocessamento em ambientes de memória compartilhada.
Introdução ao OpenMP • Estrutura de um programa OpenMP: • Em Fortran: 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: #include <omp.h> intmain () { 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. B A RR
Introdução ao OpenMP • Observações • 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: • Utilização da função omp_set_num_threads() no código Fortran ou C/C++; • Definindo a variável de ambiente OMP_NUM_THREADS, antes da execução; • 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 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 #include <stdio.h> #include <omp.h> intmain () { intnthreads, tid; #pragma omp parallel private(nthreads, tid) { 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); } } } Se tid == 0, então é o thread mestre. Obtém a quantidade de threads.
Criação de Threads Criação de 4 threads. • #include <stdio.h> • #include <omp.h> • int main() • { • double A[1000]; • #pragma omp parallel num_threads(4) • { • int ID = omp_get_thread_num(); • pooh(ID,A); • } • return 0; • } Obtém o número do thread.
Criação de Threads – Exercícios • Matematicamente sabe-se que: • Portanto, é possívelaproximaresta integral como um somatório: • Ondecadaretângulo tem largurax 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; • }
Criação de Threads – Exercícios • Solução Paralela • static long num_steps = 100000000; • double step; • int main () • { • inti,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(); • intnumthreads = omp_get_num_threads(); • double x; • 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); • } • } Região Paralela
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; • } Agrupamento dos resultados. (Redução)
Diretiva DO/for Define uma região paralela • 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. • 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.
Diretiva DO/for • Alternativamente, pode-se utilizar a seguinte sintaxe: • Exemplo: #pragma omp parallel for for (I=0;I<N;I++) Faca_algo(I); 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: • int i, j, A[MAX]; • j = 5; • for (i=0;i< MAX; i++) { • j = j+ 2*i; • A[i] = big(j); • }
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; 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 umaregiãoparalela: • É feitaumacópia local de cadavariável e inicializada com um valor quedepende do tipo de operação, e. g., naoperação de + o valor inicial é zero. • As cópiaslocaissãoreduzidas (somadas, multiplicadasetc) em um único valor que, posteriormente, sãocombinadosemumaúnicavariávelcomum. • Como fica o programaparacalcular o com a diretiva for e a redução?
Diretiva DO/for – Redução – mais detalhes • Solução Paralela • static long num_steps = 100000000; • double step; • int main () • { • inti,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++){ • x = (i-0.5)*step; • sum= sum + 4.0/(1.0+x*x); • } • pi = step * sum; • } RegiãoParalela com redução
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: • 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 • 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 Cada thread possui sua própria cópia da variável tmp com o valor inicial zero. • void useless() { • inttmp = 0; • #pragma ompparallel for firstprivate(tmp) • for (int j = 0; j < 1000; ++j) • tmp += j; • printf(“%d\n”, tmp); • }
Atributos de variáveis exemplos • void useless() { • inttmp = 0; • #pragma ompparallelfor firstprivate(tmp) lastprivate(tmp) • for (int j = 0; j < 1000; ++j) • tmp += j; • printf(“%d\n”, tmp); • } A variável tmp termina com o valor do calculado pelo último thread a terminar.
Seções • 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: • A diretiva sections define a seção do código sequencial onde será definida as seções independentes, através da diretiva section; • Cada sectioné executada por uma thread do grupo; • Existe um ponto de sincronização implícita no final da diretiva section, a menos que se especifique o atributo nowait; • 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.
Seções- Exemplo Definição de uma área de seções. • #include <omp.h> • #define N 1000 • 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 • for (i=0; i < n/2; i++) • c[i] = a[i] + b[i]; • #pragma omp section • for (i=n/2; i < n; i++) • c[i] = a[i] + b[i]; • } /* fimseções*/ • } /* fim parallel */ • } Primeira seção Segunda seção
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. • A diretiva ordered determina que as iterações do laço na região paralela, sejam executados na ordem sequêncial.
Unicidade- Exemplos 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. • #pragmaomp parallel • { • do_many_things(); • #pragmaomp single • { • exchange_boundaries(); • } • do_many_other_things(); • }
Unicidade- Exemplos • #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); • } Os threads executarão os trechos de código um de cada vez, de forma sequencial.