Tópicos Especiais em Algoritmos (Programação paralela com GPU

Propaganda
Tópicos Especiais em Algoritmos (Programação
paralela com GPU) (DCC/UFRJ)
Aula 2: Conceitos fundamentais da programação paralela
Profs. Silvana Rossetto
2 de setembro de 2016
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Fundamentos da programação paralela
A programação paralela engloba o uso de:
técnicas para dividir uma tarefa em subtarefas menores
...e mecanismos para coordenar a execução dessas
subtarefas por processadores distintos
Dependendo das caracterı́sticas do problema, e do ambiente
de execução paralela alvo (CPU multinúcleo ou GPU), o
programador deve selecionar as alternativas mais adequadas
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Exemplo inicial
Considere a operação C = A * k + B, sendo A, B e C
vetores de tamanho N e k um número (operação que aparece
com frequência em problemas de álgebra linear
computacional)
Para encontrar o vetor de saı́da C , precisamos calcular cada
elemento desse vetor fazendo: C[i] = A[i] * k + B[i],
0<=i<N
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Algoritmo sequencial
void somaVetoresSeq(const float a[], const float b[],
float c[], float k, int n) {
for(int i=0; i<n; i++) {
c[i] = a[i] * k + b[i];
}
}
void main() {
float a[N], b[N], c[N];
//inicializa os vetores a e b
...
somaVetoresSeq(a, b, c, 2.0, N);
}
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Algoritmo paralelo
void calculaElementoVetor(const float a[],
const float b[], float c[], float k, int pos) {
c[pos] = k * a[pos] + b[pos];
}
void main() {
float a[N], b[N], c[N];
//inicializa os vetores a e b
...
//faz C = k * A + B
for(int i=0; i<N; i++) {
//dispara um fluxo de execuç~
ao f para executar:
//
calculaElementoVetor(a, b, c, 2.0, i)
}
}
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Execução sequencial versus execução paralela
O que fizemos foi reduzir a complexidade de processamento de
O(N) para O(1), assumindo que N fluxos de execução poderão
estar ativos ao mesmo tempo
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Exemplo inicial
..mas se não tivermos processadores em número suficiente para
permitir que todos os fluxos de execução estejam ativos ao mesmo
tempo?
vale a pena pensar em outras formas de dividir a tarefa
principal?
quais seriam essas outras formas?
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Algoritmo paralelo (alternativa 1)
void calculaElementosConsVetor(const float a[],const float b[],
float c[], float k, int inicio, int fim) {
for(int i=inicio; i<fim; i++)
c[i] = k * a[i] + b[i];
}
void main() {
float a[N], b[N], c[N];
int i, inicio, fim;
//inicializa os vetores a e b
...
//faz C = k * A + B
for(i=0; i<P; i++) {
inicio = i * (N/P);
//o último fluxo trata os elementos restantes
if (i<P-1) fim = inicio + (N/P);
else fim = N;
//dispara um fluxo de execuç~
ao f para executar:
//calculaElementosConsVetor(a, b, c, 2.0, inicio, fim);
} }
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Algoritmo paralelo (alternativa 2)
void calculaElementosInterVetor(const float a[], const float b[]
float c[], float k, int inicio, int salto, int n) {
int i;
for(i=inicio; i<n; i=i+salto) {
c[i] = k * a[i] + b[i];
}
}
void main() {
float a[N], b[N], c[N];
//inicializa os vetores a e b
...
//faz C = k * A + B
for(int i=0; i<P; i++) {
//dispara um fluxo de execuç~
ao f para executar:
//calculaElementosInterVetor(a, b, c, 2.0, i, P, N);
}
}
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Balanceamento de carga entre as threads
Uma preocupação quando projetamos um algoritmo paralelo é
garantir que todas as unidades de processamento
recebam a mesma carga de trabalho para aproveitar todos
os recursos de processamento disponı́veis
As alternativas de paralelização mostradas garantem o
balanceamento de carga entre as unidades de processamento?
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Padrão de acesso à memória
O padrão de acesso à memória varia de uma alternativa para a
outra, o que poderá fazer com que o desempenho de cada uma
delas — em termos de tempo de acesso à memória — varie de
forma significativa
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Outro exemplo de problema
Considere agora o problema de somar todos os elementos de
um vetor:
float somaElemVetorSeq(const float a[], int n) {
float soma = 0;
for(int i=0; i<n; i++) {
soma = soma + a[i];
}
return soma;
}
void main() {
float a[N], soma_total;
//inicializa o vetor a
...
soma_total = somaElemVetorSeq(a, N);
printf("soma total = %f", soma_total);
}
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Soma os elementos de um vetor
Exercı́cio
Escreva um algoritmo para paralelizar esse problema.
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
uma proposta de algoritmo paralelo (para CPU)
float soma_total = 0;
//Soma uma parte dos n elementos do vetor a
void somaGrupoConsElemVetor(const float a[],
int inicio, int fim) {
float soma_local = 0;
for(int i=inicio; i<fim; i++)
soma_local = soma_local + a[i];
// !!! solicita exclus~
ao mútua !!!
soma_total = soma_total + soma_local;
// !!! libera a exclus~
ao mútua !!!
}
...
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
uma proposta de algoritmo paralelo (para CPU)
...
void main() {
float a[N]; int i, inicio, fim;
//inicializa o vetor a
...
//soma os n elementos de a
for(i=0; i<P; i++) {
inicio = i * (N/P);
//o último fluxo trata os elementos restantes
if (i<P-1) fim = inicio + (N/P);
else fim = N;
//dispara um fluxo de execuç~
ao f para executar:
//somaGrupoConsElemVetor(a, inicio, fim);
}
//!!!espera todos os fluxos de execuç~
ao terminarem !!!
printf("soma total = %f", soma_total);
}
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Sincronização das threads
sincronização por exclusão mútua
sincronização por condição
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Sincronização por barreira
tipo particular de sincronização por condição que aparece em
problemas que requerem que todos os fluxos de execução (ou um
grupo deles) sicronizem suas ações a cada passo do algoritmo
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Definição de programação paralela
Navarro et al. definem a programação paralela como a tarefa
de resolver um problema de tamanho n dividindo o seu
domı́nio em k ≥ 2 (k ∈ N) partes que deverão ser executadas
em p processadores fı́sicos simultaneamente
Seguindo essa definição, um problema PD com domı́nio D é
dito paralelizável se for possı́vel decompor PD em k
subproblemas:
D = d1 ⊕ d2 ⊕ · · · ⊕ dk =
k
X
di
i=1
Dependendo das caracterı́sticas do problema, há diferentes
formas de realizar essa decomposição
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Paralelismo de dados ou paralelismo de tarefas
Dizemos que o problema PD é um problema com paralelismo
de dados se D é composto de elementos de dados e é
possı́vel aplicar uma função f (· · · ) para todo o domı́nio:
f (D) = f (d1 ) ⊕ f (d2 ) ⊕ · · · ⊕ f (dk ) =
k
X
f (di )
i=1
Por outro lado, se D é composto por funções e a solução do
problema consiste em aplicar cada função sobre um fluxo de
dados comum, dizemos que o problema PD é um problema
com paralelismo de tarefas:
D(S) = d1 (S) ⊕ d2 (S) ⊕ · · · ⊕ dk (S) =
k
X
di (S)
i=1
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Metodologia para projetar algoritmos paralelos
Projetar e implementar algoritmos paralelos não é uma tarefa
simples e não há uma regra geral para desenvolver algoritmos
paralelos perfeitos
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Etapas propostas por Ian Foster
Particionamento: a tarefa que deve ser executada e o
conjunto de dados associado a ela são decompostos em
tarefas menores
o objetivo é identificar oportunidades de execução paralela
caracterizar o domı́nio do problema (com “paralelismo de
dados” ou “paralelismo de tarefas”) é fundamental para
escolher uma boa estratégia de particionamento
Comunicação: nessa etapa, determina-se a comunicação
requerida para coordenar a execução da tarefa e define-se as
estruturas e algoritmos de comunicação mais apropriados
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Etapas propostas por Ian Foster
Aglomeração: as subtarefas e estruturas de comunicação
definidas nas etapas anteriores são avaliadas com respeito aos
requisitos de desempenho e custos de implementação e, se
necessário, as subtarefas são combinadas em tarefas maiores
para melhorar o desempenho ou reduzir os custos de
desenvolvimento
Mapeamento: cada tarefa é designada para uma unidade de
processamento de uma forma que satisfaça a meta de
maximizar o uso da capacidade de processamento paralela
disponı́vel e minimizar os custos de comunicação e gerência
o mapeamento pode ser feito de forma estática ou
determinado em tempo de execução
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
GPGPU versus GPU Computing
“GPGPU”(General-Purpose computing on GPUs)
Até 2006, os chips gráficos eram muito difı́ceis de programar por
conta da necessidade de usar a API gráfica para acesso ao
processadores (técnicas OpenGL e Direct3D) (ver
http://gpgpu.org)
“GPU Computing”
Em 2007, com o lançamento de CUDA e mudanças no hardware
(chips G-80 em diante), a interface de programação passou a não
depender mais da API gráfica
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Sistemas de “computação massiva”
Combinam CPUs multicore e GPUs manycore
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
CPUs multicores X GPUs manycores
CPU multicore
Projeto otimizado para melhorar o desempenho de código
sequencial
Lógica de controle sofisticada para que instruções de uma
única thread executem em paralelo (ou fora da sua ordem
sequencial) enquanto mantém a aparência de execução
sequencial
Memória cache grande para reduzir a latência de acesso
aos dados e instruções das aplicações mais complexas (largura
de banda de acesso à memória <20GB/s)
Dificuldade de alterar o modelo de acesso à memória (para
atender requisitos de sistemas operacionais e sistemas legados)
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
CPUs multicores X GPUs manycores
GPU manycore
Projeto otimizado para execução de um número muito
grande de threads
Lógica de controle simplificada na execução de cada thread
Memória cache pequena (apenas para ajudar nos requisitos
de largura de banda i.e., threads acessando os mesmos dados
não precisam ir todas a DRAM)
Largura de banda de acesso a DRAM 80GB/s, e para
comunicação com a CPU 4GB/s (em cada direção)
O hardware aproveita o grande número de threads para encontrar
trabalho para fazer quando parte das threads estão esperando pela
latência de acesso à memória
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
CPUs multicores X GPUs manycores
Figura: Programming Massively Parallel Processors: A Hands-on Approach,
2010 David B. Kirk/NVIDIA Corporation and Wen-mei Hwu. Elsevier Inc.
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Referências bibliográficas
1
C. A. Navarro, N. Hitschfeld-Kahler e L. Mateu, A Survey on
Parallel Computing and its Applications in Data-Parallel
Problems Using GPU Architectures, Communications in
Computational Physics, vol 15, 2014
2
I. Foster, Designing and building parallel programs : concepts
and tools for parallel software engineering, Addison-Wesley,
1995
Profs. Silvana Rossetto
Conceitos fundamentais da programação paralela
Download