Computação de alto desempenho
Aqui um dos principais temas é paralelização.
OpenMP
OpenMP é uma biblioteca para programação multi-processo, que permite tornar um código paralelo sem grandes alterações
Parallel
A diretiva indica uma região que será executada por múltiplas threads.
Essa diretiva suporta duas diretivas: e
Parâmetros que podem ser utilizados com essa diretiva:
Exemplo executando uma seção em paralelo:
#pragma omp parallel num_threads(4)
{
printf("Hello world (%d)\n", omp_get_thread_num());
}
Saída possível:Hello world (0)
Hello world (2)
Hello world (1)
Hello world (3)
Single
A diretiva indica uma região que será executada por apenas uma thread. Ao final do bloco, existe uma barreira implícita a menos que
seja especificado.
Parâmetros que podem ser utilizados com essa diretiva:
Exemplo com uma seção que executa apenas em uma thread:
#pragma omp parallel num_threads(4)
{
#pragma omp single
printf("Single (%d)\n", omp_get_thread_num());
printf("Hello world (%d)\n", omp_get_thread_num());
}
Saída possível: (note o código é executado sequencialmente dentro da thread, então Single
, sempre estará antes do Hello world
de sua thread)Single (2)
Hello world (3)
Hello world (0)
Hello world (1)
Hello world (2)
Master
A diretiva indica uma região que será executada pela thread principal. Não existe nenhuma barreira implícita.
Essa diretiva não suporta nenhum parâmetro
Exemplo com uma seção que executa apenas em uma thread:
#pragma omp parallel num_threads(4)
{
#pragma omp master
printf("Master (%d)\n", omp_get_thread_num());
printf("Hello world (%d)\n", omp_get_thread_num());
}
Saída possível: (note que, como não há barreira, as linhas posteriores podem ser executadas em outras threads antes do
)Hello world (2)
Master (0)
Hello world (0)
Hello world (1)
Hello world (3)
Reduction
Exemplo usando reduction
Produto escalar de dois vetores comdouble
double dot(const double *a, const double *b, size_t len) {
double res = 0.;
#pragma omp parallel for reduction(+ : res) default(none) shared(len, a, b) num_threads(8) schedule(static)
for (size_t i = 0; i < len; ++i)
res += a[i] * b[i];
return res;
}
Essa operação pode ser paralelizada facilmente. A diretiva cria uma zona "multi-thread", nessa zona, a diretiva
faz com que o loop a seguir seja executado de forma paralela, com cada thread executando uma parte do intervalo. E a diretiva
realiza uma operação "reduction" somando a variável local
res
de todas as threads, e atribuindo à variável global res
.
Programa completo
dot_product.c#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
double *new_vector(size_t size) {
double *vec = malloc(sizeof(double) * size);
for (size_t i = 0; i < size; ++i)
vec[i] = 1.0f;
return vec;
}
double dot(const double *a, const double *b, size_t len) {
double res = 0.;
#pragma omp parallel for reduction(+ : res) default(none) shared(len, a, b) num_threads(8) schedule(static)
for (size_t i = 0; i < len; ++i)
res += a[i] * b[i];
return res;
}
double dot_seq(const double *a, const double *b, size_t len) {
double res = 0.;
for (size_t i = 0; i < len; ++i)
res += a[i] * b[i];
return res;
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s LEN\n", argv[0]);
}
size_t len = strtoull(argv[1], NULL, 10);
double *a = new_vector(len), *b = new_vector(len);
double start = omp_get_wtime();
double result = dot(a, b, len);
double end = omp_get_wtime();
double time = end - start;
printf("Parallel dot product of %zu numbers: %.2f. Took %lf seconds\n", len, result, time);
double start_seq = omp_get_wtime();
double result_seq = dot_seq(a, b, len);
double end_seq = omp_get_wtime();
double time_seq = end_seq - start_seq;
printf("Sequencial dot product of %zu numbers: %.2f. Took %lf seconds\n", len, result_seq, time_seq);
printf("Speedup: %.3fx\n", time_seq / time);
free(a);
free(b);
return 0;
}
> gcc dot_product.c -fopenmp
> ./a.out 100000000
Parallel dot product of 100000000 numbers: 100000000.00. Took 0.064056 seconds
Sequencial dot product of 100000000 numbers: 100000000.00. Took 0.251648 seconds
Speedup: 3.929x
Clauses
Private
Especifica que cada thread deve ter a sua própria variável.private(var[, var])
Firstprivate
Especifica que cada thread deve ter a sua própria variável, e que essa variável deve ser inicializada para o valor da variável já existentefirstprivate(var[, var])
Default
Define o comportamento de variáveis não especificadasdefault(shared | none)
shared torna todas as variáveis não especificadas compartilhadas, e none torna as variáveis não especificadas inacessíveis.
Num_Threads
Define o número de threads em um grupo de threadsnum_threads(num)
Possui a mesma funcionalidade que a função omp_set_num_threads
Nowait
Remove uma barreira implícita em uma diretivanowait