Slides Lição 4, CPAD, Como se Conduzir na Caminhada, 2Tr24.pptx
OpenACC: aceleração de códigos com diretivas
1. OpenACC: um promissor standard
para aceleração de códigos
Pedro Pais Lopes
NVIDIA GPU Computing Developer Forum
Curitiba, 17/07/2012
2. O que é OpenACC?
• Diretivas de compilação, na forma de pragmas
(C/C++) ou comentários (Fortran) de alto nível,
que orientam o compilador a executar trechos
de código em um acelerador
• Esforço conjunto PGI + Cray + NVIDIA + CAPS,
visando integrar padrão OpenMP
• Vide padrão em www.openacc.org
3. Sintaxe
• C:
#pragma acc nome_diretiva [cláusula [,cláusula]…]
bloco estruturado de código
• Fortran:
!$acc nome_diretiva [cláusula [,cláusula]…]
bloco estruturado de código
!$acc end nome_diretiva
4. Exemplo #1: Jacobi
• Converge iterativamente para um valor a
partir da média dos pontos vizinhos
0°C
A(i+1,j)
0°C 0°C
A(i-1,j) A(i,j) A(i+1,j)
A(i,j-1)
100°C
5. Código Fortran
do while ( err > tol .and. iter < iter_max )
err=0.0
do j=1,m
do i=1,n
Anew(i,j) = .25 * (A(i+1, j ) + A(i-1, j ) + &
A(i , j-1) + A(i , j+1))
err = max(err, Anew(i,j) - A(i,j))
end do
end do
do j=1,m-2
do i=1,n-2
A(i,j) = Anew(i,j)
end do
end do
iter = iter +1
end do
6. Inserindo OpenMP
do while ( err > tol .and. iter < iter_max )
err=0.0
!$omp parallel do private(i,j) reduction(max:err)
do j=1,m
do i=1,n
Anew(i,j) = .25 * (A(i+1, j ) + A(i-1, j ) + &
A(i , j-1) + A(i , j+1))
err = max(err, Anew(i,j) - A(i,j))
end do
end do
!$omp end parallel do
!$omp parallel do private(i,j)
do j=1,m-2
do i=1,n-2
A(i,j) = Anew(i,j)
end do
end do
!$omp end parallel do
iter = iter +1
end do
7. Código em Fortran e OpenACC
Um kernel por ninho de laços
do while ( err > tol .and. iter < iter_max )
err=0.0
!$acc kernels reduction(max:err) Trafego GPU-CPU gerado
do j=1,m automaticamente pelo compilador
do i=1,n
Anew(i,j) = .25 * (A(i+1, j ) + A(i-1, j ) + &
A(i , j-1) + A(i , j+1))
err = max(err, Anew(i,j) - A(i,j))
end do
end do
!$acc end kernels barreira
!$acc kernels
do j=1,m-2 Outro kernel
do i=1,n-2
A(i,j) = Anew(i,j)
end do
end do
!$acc end kernels
iter = iter +1
end do
8. Vantagens
• Código CUDA gerado automaticamente
• Mantém portabilidade entre o código sequencial e o
código paralelo
– Chave de compilação define se compilador gera código
para GPU ou não
• Trafego de dados entre a CPU e a GPU determinado
“automaticamente” pelo compilador
9. CPU: Intel Xeon X5680 Desempenho GPU: NVIDIA Tesla M2070
6 Cores @ 3.33GHz
(fonte: NVIDIA)
Execução Tempo (s) Speedup
CPU 1 OpenMP thread 69.80 --
CPU 2 OpenMP threads 44.76 1.56x
CPU 4 OpenMP threads 39.59 1.76x
CPU 6 OpenMP threads 39.71 1.76x
OpenACC GPU 1 thread 162.16 0.43x OPA!
10. Compilador fez o melhor que pode
• Mas ele NÃO pode desrespeitar o código
1. Enquanto condicional do while for satisfeita…
2. copia A para a placa
3. Computa primeiro laço
4. Copia Anew para CPU
5. Copia Anew para placa
6. Computa segundo laço
7. Copia A para a CPU
8. Volta para 1.
11. Código em Fortran e OpenACC
do while ( err > tol .and. iter < iter_max )
err=0.0
!$acc kernels reduction(max:err)
do j=1,m
do i=1,n
Anew(i,j) = .25 * (A(i+1, j ) + A(i-1, j ) + &
A(i , j-1) + A(i , j+1))
err = max(err, Anew(i,j) - A(i,j))
end do
end do
!$acc end kernels
!$acc kernels
do j=1,m-2
do i=1,n-2
A(i,j) = Anew(i,j)
end do
end do
!$acc end kernels
iter = iter +1
end do
12. Melhoria #1
do while ( err > tol .and. iter < iter_max )
err=0.0
!$acc kernels reduction(max:err)
do j=1,m
do i=1,n
Anew(i,j) = .25 * (A(i+1, j ) + A(i-1, j ) + &
A(i , j-1) + A(i , j+1))
err = max(err, Anew(i,j) - A(i,j))
end do
end do
Juntar os dois kernels em um elimina tráfego
CPU/GPU de A e Anew entre os dois aninamentos
do j=1,m-2
do i=1,n-2
A(i,j) = Anew(i,j)
end do
end do
!$acc end kernels
iter = iter +1
end do
13. Podemos ir além (melhoria #2)
Envie A da CPU para a GPU no início do
laço e envie de volta ao final do laço
!$acc data copy(A), create(Anew) Crie e mantenha Anew na GPU
do while ( err > tol .and. iter < iter_max )
err=0.0
!$acc kernels reduction(max:err)
do j=1,m
do i=1,n
Anew(i,j) = .25 * (A(i+1, j ) + A(i-1, j ) + &
A(i , j-1) + A(i , j+1))
err = max(err, Anew(i,j) - A(i,j))
end do
end do
do j=1,m-2
do i=1,n-2
A(i,j) = Anew(i,j)
end do
end do
!$acc end kernels
iter = iter +1
end do
!$acc end data
14. CPU: Intel Xeon X5680 Desempenho GPU: NVIDIA Tesla M2070
6 Cores @ 3.33GHz
(fonte: NVIDIA)
Execução Tempo (s) Speedup
CPU 1 OpenMP thread 69,80 --
CPU 2 OpenMP threads 44,76 1,56x
CPU 4 OpenMP threads 39,59 1,76x
CPU 6 OpenMP threads 39,71 1,76x
5,11x
OpenACC GPU 13,65
BOM!
15. Resumo
• Foram 3 linhas a mais para 5,11x de speedup
– Inclusão de comentários: outros compiladores
ignorarão esta linha e código segue inalterado
• Uso de diretivas deve ter “precisão cirúrgica”
• Corretude da computação: não somente se
preocupar com “race conditions” mas
também com gerenciamento de dados
– Lembrar-se SEMPRE que temos cópia LOCAL e
cópia REMOTA: memória não estará inicializada
16. Sumário
• OpenACC é forma promissora de programar
aceleradores inserindo diretivas no programa original
• Como em OpenMP, é:
– Incremental
– Portátil
• Muito mais simples e fácil que CUDA, preservando o
investimento na codificação original