Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Introdução ao Processamento Paralelo (4.2)

795 views

Published on

Minicurso ofericido durante a III Semana de Inverno de Geofisica, IMECC/UNICAMP, 2012, por Jairo Panetta (ITA/ICE).

Published in: Education
  • Be the first to comment

  • Be the first to like this

Introdução ao Processamento Paralelo (4.2)

  1. 1. OpenACC: um promissor standard para aceleração de códigos Pedro Pais LopesNVIDIA GPU Computing Developer Forum Curitiba, 17/07/2012
  2. 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. 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. 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°CA(i-1,j) A(i,j) A(i+1,j) A(i,j-1) 100°C
  5. 5. Código Fortrando 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 +1end do
  6. 6. Inserindo OpenMPdo 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 +1end do
  7. 7. Código em Fortran e OpenACC Um kernel por ninho de laçosdo 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 +1end do
  8. 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. 9. CPU: Intel Xeon X5680 Desempenho GPU: NVIDIA Tesla M20706 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. 10. Compilador fez o melhor que pode• Mas ele NÃO pode desrespeitar o código1. Enquanto condicional do while for satisfeita…2. copia A para a placa3. Computa primeiro laço4. Copia Anew para CPU5. Copia Anew para placa6. Computa segundo laço7. Copia A para a CPU8. Volta para 1.
  11. 11. Código em Fortran e OpenACCdo 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 +1end do
  12. 12. Melhoria #1do 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 +1end do
  13. 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 GPUdo 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 +1end do!$acc end data
  14. 14. CPU: Intel Xeon X5680 Desempenho GPU: NVIDIA Tesla M20706 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. 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. 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

×