OpenMP - dcce.ibilce.unesp.braleardo/cursos/hpc/openMP2020.… · Programa OpenMP começa com um...

Preview:

Citation preview

OpenMP

Aleardo Manacero Jr.

Copyright Notice

Material parcialmente retirado de“Introduction to OpenMP”, por Rudi Eigenmann -- eigenman@ecn.purdue.edu, Bob Kuhn -- bob@kai.com, Tim Mattson -- timothy.g.mattson@intel.com, e Ramesh Menon -- menon@sgi.com

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

Recommended