1. O documento discute a velocidade de Python em comparação com Fortran para tarefas de programação numérica.
2. Inicialmente, um programa simples em Fortran é mais rápido do que a versão em Python/NumPy. No entanto, otimizações como alterar a ordem de loops podem tornar Python quase tão rápido quanto Fortran.
3. A implementação BLAS (Basic Linear Algebra Subprograms) é discutida, que fornece rotinas altamente otimizadas para álgebra linear. Isto torna Python com NumPy qu
1. Python tão rápido quanto Fortran?
Nelson Luís Dias1
1
Professor Associado, Departamento de Engenharia Ambiental, Universidade
Federal do Paraná, e-mail: nldias@ufpr.br. url: www.lemma.ufpr.br/nldias
3 de janeiro de 2013
Lemma
UFPR
2. 1
Pesquisa e programação
• Programas (em geral) são meios para fazer pesquisa.
Lemma
UFPR
3. 1
Pesquisa e programação
• Programas (em geral) são meios para fazer pesquisa.
• Portanto, a pergunta principal de pesquisa/programação é:
Quais são as melhores ferramentas para cada tarefa?
Lemma
UFPR
4. 1
Pesquisa e programação
• Programas (em geral) são meios para fazer pesquisa.
• Portanto, a pergunta principal de pesquisa/programação é:
Quais são as melhores ferramentas para cada tarefa?
• Não há uma única resposta, porque, como sempre, o resultado depende do
conhecimento que o usuário tem da ferramenta.
Lemma
UFPR
5. 1
Pesquisa e programação
• Programas (em geral) são meios para fazer pesquisa.
• Portanto, a pergunta principal de pesquisa/programação é:
Quais são as melhores ferramentas para cada tarefa?
• Não há uma única resposta, porque, como sempre, o resultado depende do
conhecimento que o usuário tem da ferramenta.
• Nesta apresentação: algumas técnicas para fazer bom uso de Fortran ×
Python
Lemma
UFPR
6. 2
Principais pontos desta apresentação:
• Python é conveniente, mas é lento.
Lemma
UFPR
7. 2
Principais pontos desta apresentação:
• Python é conveniente, mas é lento.
• Fortran é muito menos conveniente, e verborrágico, mas é muito rápido.
Lemma
UFPR
8. 2
Principais pontos desta apresentação:
• Python é conveniente, mas é lento.
• Fortran é muito menos conveniente, e verborrágico, mas é muito rápido.
• Mas BLAS muda tudo, e torna Python (com Numpy e Numba) quase tão rápido
quanto — ou até mesmo mais rápido que — Fortran.
Lemma
UFPR
9. 3
Parte I: Fortran × Python em Linux, sem BLAS
Lemma
UFPR
10. 3
Parte I: Fortran × Python em Linux, sem BLAS
Fortran:
• várias linguagens em uma (F66, F77,
F90, F95, F03, F08)
• muito grande e mais difícil de aprender e
dominar
• muito popular entre cientistas e enge-
nheiros
• muito rápido (compilado)
Lemma
UFPR
11. 3
Parte I: Fortran × Python em Linux, sem BLAS
Fortran: Python:
• várias linguagens em uma (F66, F77, • uma única linguagem
F90, F95, F03, F08) • muitas bibliotecas padrão, e muitas ex-
• muito grande e mais difícil de aprender e tensões
dominar • pequena e simples
• muito popular entre cientistas e enge- • muito lento (interpretado)
nheiros • Numpy, Scipy, SymPy, rPython: gigan-
• muito rápido (compilado) tescas bibliotecas numéricas e simbóli-
cas pré-compiladas (em Fortran ou C) e
disponíveis
Lemma
UFPR
12. 4
Gênesis
1. No princípio, criou Deus os céus e a terra.
2. E a terra era sem forma e vazia; e havia trevas sobre a face do abismo; e o
Espírito de Deus se movia sobre a face das águas.
3. E disse Deus: Haja luz Fortran. E houve luz.
4. E viu Deus que era boa a luz; e fez Deus separação entre a luz Fortran e as
trevas tabelas de logaritmos.
5. E Deus chamou à luz Dia; e às trevas chamou Noite. E foi a tarde e a manhã:
o dia primeiro.
Lemma
UFPR
13. 4
Gênesis
1. No princípio, criou Deus os céus e a terra.
2. E a terra era sem forma e vazia; e havia trevas sobre a face do abismo; e o
Espírito de Deus se movia sobre a face das águas.
3. E disse Deus: Haja luz Fortran. E houve luz.
4. E viu Deus que era boa a luz; e fez Deus separação entre a luz Fortran e as
trevas tabelas de logaritmos.
5. E Deus chamou à luz Dia; e às trevas chamou Noite. E foi a tarde e a manhã:
o dia primeiro.
Fortran (1957) foi a primeira linguagem de programação de “alto nível” (que usava
um compilador).
Lemma
UFPR
14. 5
Portanto, comece com Fortran
1 program cacumij
2 integer , parameter :: n = 5000
3 real ( kind =8) :: a (0: n -1 ,0: n -1)
4 integer ( kind =4) :: i ,j
5 call cpu_time ( t1 )
6 do i = 0,n -1
7 do j = 0,n -1
8 a(i ,j ) = i +j
9 end do
10 end do
11 call cpu_time ( t2 )
12 write (* ,*) t2 - t1
13 end program
Lemma
UFPR
15. 5
Portanto, comece com Fortran
1 program cacumij
2 integer , parameter :: n = 5000
3 real ( kind =8) :: a (0: n -1 ,0: n -1)
4 integer ( kind =4) :: i ,j
5 call cpu_time ( t1 )
6 do i = 0,n -1
7 do j = 0,n -1
8 a(i ,j ) = i +j
9 end do
10 end do
11 call cpu_time ( t2 )
12 write (* ,*) t2 - t1
13 end program
gfortran cacumij . f90 -o cacumij
./ cacumij
0.552033007
Lemma
UFPR
16. 6
E agora compare com Python + Numpy
1 # !/ usr / bin / python
2 from numpy import arange , array , int32 , float64 , zeros
3 from time import time
4 n = 5000
5 t1 = time ()
6 ix = arange (n , dtype = int32 )
7 iy = arange (n , dtype = int32 )
8 a = zeros ((n , n), float64 )
9 a = ix [: , None ] + iy [ None ,:]
10 t2 = time ()
11 print t2 - t1
Lemma
UFPR
17. 6
E agora compare com Python + Numpy
1 # !/ usr / bin / python
2 from numpy import arange , array , int32 , float64 , zeros
3 from time import time
4 n = 5000
5 t1 = time ()
6 ix = arange (n , dtype = int32 )
7 iy = arange (n , dtype = int32 )
8 a = zeros ((n , n), float64 )
9 a = ix [: , None ] + iy [ None ,:]
10 t2 = time ()
11 print t2 - t1
./ cacum . py
0.309801101685
Lemma
UFPR
18. 7
Observações
• O algoritmo
aij = i + j
aparentemente não é vetorizável: BLAS (que surgirá daqui a pouco) não se
aplica.
• No entanto, a sintaxe de cacum.py é vetorial (e um pouco estranha para os
não-iniciados).
Lemma
UFPR
19. 7
Observações
• O algoritmo
aij = i + j
aparentemente não é vetorizável: BLAS (que surgirá daqui a pouco) não se
aplica.
• No entanto, a sintaxe de cacum.py é vetorial (e um pouco estranha para os
não-iniciados).
• cacum.py é quase duas vezes mais rápido que cacumij.f90.
Lemma
UFPR
20. 7
Observações
• O algoritmo
aij = i + j
aparentemente não é vetorizável: BLAS (que surgirá daqui a pouco) não se
aplica.
• No entanto, a sintaxe de cacum.py é vetorial (e um pouco estranha para os
não-iniciados).
• cacum.py é quase duas vezes mais rápido que cacumij.f90.
• o que está errado?
Lemma
UFPR
21. 8
Troque i com j
1 program cacumji
2 integer , parameter :: n = 5000
3 real ( kind =8) :: a (0: n -1 ,0: n -1)
4 integer ( kind =4) :: i ,j
5 call cpu_time ( t1 )
6 do j = 0,n -1
7 do i = 0,n -1
8 a(i ,j ) = i +j
9 end do
10 end do
11 call cpu_time ( t2 )
12 write (* ,*) t2 - t1
13 end program
Lemma
UFPR
22. 8
Troque i com j
1 program cacumji
2 integer , parameter :: n = 5000
3 real ( kind =8) :: a (0: n -1 ,0: n -1)
4 integer ( kind =4) :: i ,j
5 call cpu_time ( t1 )
6 do j = 0,n -1
7 do i = 0,n -1
8 a(i ,j ) = i +j
9 end do
10 end do
11 call cpu_time ( t2 )
12 write (* ,*) t2 - t1
13 end program
gfortran cacumji . f90 -o cacumji
./ cacumji
0.212011993
Lemma
UFPR
23. 9
E dê mais uma tentativa a Python
1 # !/ usr / bin / python
2 from numpy import arange , array , int32 , zeros
3 from time import time
4 n = 5000
5 t1 = time ()
6 a = zeros ((n , n), ' f8 ')
7 def aom (a ):
8 for i in range (n ):
9 for j in range (n ):
10 a[i ,j ] = i +j
11 aom (a )
12 t2 = time ()
13 print t2 - t1
Lemma
UFPR
24. 9
E dê mais uma tentativa a Python
1 # !/ usr / bin / python
2 from numpy import arange , array , int32 , zeros
3 from time import time
4 n = 5000
5 t1 = time ()
6 a = zeros ((n , n), ' f8 ')
7 def aom (a ):
8 for i in range (n ):
9 for j in range (n ):
10 a[i ,j ] = i +j
11 aom (a )
12 t2 = time ()
13 print t2 - t1
./ cacumij - slow . py
4.56774091721
Lemma
UFPR
25. 10
. . . e mais uma:
1 # !/ usr / bin / python
2 from numpy import arange , array , int32 , zeros
3 from time import time
4 from numba import jit , float64
5 n = 5000
6 t1 = time ()
7 a = zeros ((n , n), ' f8 ')
8 @jit ( argtypes =[ float64 [: ,:]])
9 def aom (a ):
10 for i in range (n ):
11 for j in range (n ):
12 a[i ,j ] = i +j
13 aom (a )
14 t2 = time ()
15 print t2 - t1
Lemma
UFPR
26. 10
. . . e mais uma:
1 # !/ usr / bin / python
2 from numpy import arange , array , int32 , zeros
3 from time import time
4 from numba import jit , float64
5 n = 5000
6 t1 = time ()
7 a = zeros ((n , n), ' f8 ')
8 @jit ( argtypes =[ float64 [: ,:]])
9 def aom (a ):
10 for i in range (n ):
11 for j in range (n ):
12 a[i ,j ] = i +j
13 aom (a )
14 t2 = time ()
15 print t2 - t1
./ cacumij . py
0.290515899658
Lemma
UFPR
27. 11
Conclusões desta parte
• Um bom conhecimento dos detalhes de uma linguagem (no caso, como Fortran
armazena matrizes) é muitas vezes necessário para otimizar um programa.
• A ordem em que uma matriz é percorrida (ij, ji) importa muito, porque a CPU
é muito mais rápida do que qualquer memória. A diferença de velocidade está
aumentando: a tecnologia está tornando as CPUs relativamente mais e mais
rápidas que as memórias.
• Depois de otimizado, Fortran ainda é (neste exemplo) 33% mais rápido.
• No entanto, obtivemos a mesma ordem de grandeza de tempo de CPU com
Python, de duas maneiras diferentes (sem e com numba).
• Compiladores “just in time” (numba) estão chegando próximos do desempenho
de Fortran.
Lemma
UFPR
28. 12
O efeito que BLAS faz. . .
BLAS = Basic Linear Algebra Subprograms.
BLAS é um conjunto altamente otimizado de rotinas para realizar operações de
álgebra linear.
Existem hoje em dia várias implementações (http://en.wikipedia.org/wiki/
Basic_Linear_Algebra_Subprograms):
Accelerate, ACML, C++ AMP BLAS, ATLAS, ESSL, Eigen BLAS, Goto BLAS, HP
MLIB, Intel MKL, MathKeisan, Netlib BLAS, Netlib CBLAS, PDLIB/SX, SCSL, Sun
Performance Library, SurviveGotoBLAS2, OpenBLAS, cuBLAS,
Em Linux: é relativamente fácil instalar um BLAS pré-compilado (nunca tão bom
quanto compilar e ajustar para a própria máquina). Minha escolha: ATLAS.
Em Windows: um pequeno pesadelo, se não houver um compilador comercial de
Fortran. Minha escolha: MingGW (compilador C e Fortran) + OpenBLAS.
Lemma
UFPR
29. 13
Produto de 2 matrizes em Fortran:
1 program cabum
2 integer , parameter :: size = 1000
3 real ( kind =8) :: a( size , size ), b( size , size ), c( size , size )
4 integer ( kind =4) :: seed
5 call random_seed ()
6 call random_number (a )
7 call random_number (b )
8 call cpu_time ( t1 )
9 c = matmul (a , b)
10 call cpu_time ( t2 )
11 write (* ,*) t2 - t1
12 end program
Lemma
UFPR
30. 13
Produto de 2 matrizes em Fortran:
1 program cabum
2 integer , parameter :: size = 1000
3 real ( kind =8) :: a( size , size ), b( size , size ), c( size , size )
4 integer ( kind =4) :: seed
5 call random_seed ()
6 call random_number (a )
7 call random_number (b )
8 call cpu_time ( t1 )
9 c = matmul (a , b)
10 call cpu_time ( t2 )
11 write (* ,*) t2 - t1
12 end program
sem BLAS: gfortran - o3 cabum . f90 -o cabum - noblas
./ cabum - noblas ; 1.62810099
Lemma
UFPR
32. 14
Produto de 2 matrizes em Python/Numpy:
1 # !/ usr / bin / python
2 import numpy
3 n = 1000
4 a = numpy . random . rand (n ,n )
5 b = numpy . random . rand (n ,n )
6 from time import time
7 t1 = time ()
8 c = numpy . dot (a , b)
9 t2 = time ()
10 print t2 - t1
Lemma
UFPR
33. 14
Produto de 2 matrizes em Python/Numpy:
1 # !/ usr / bin / python
2 import numpy
3 n = 1000
4 a = numpy . random . rand (n ,n )
5 b = numpy . random . rand (n ,n )
6 from time import time
7 t1 = time ()
8 c = numpy . dot (a , b)
9 t2 = time ()
10 print t2 - t1
./ cabum . py
0.295262813568
Lemma
UFPR
34. 15
Conclusões, com BLAS
• BLAS faz toda a diferença: não saia de casa sem ele.
Lemma
UFPR
35. 15
Conclusões, com BLAS
• BLAS faz toda a diferença: não saia de casa sem ele.
• BLAS torna um programa Fortran otimizado para multiplicar matrizes 6 vezes
mais rápido.
Lemma
UFPR
36. 15
Conclusões, com BLAS
• BLAS faz toda a diferença: não saia de casa sem ele.
• BLAS torna um programa Fortran otimizado para multiplicar matrizes 6 vezes
mais rápido.
• Numpy tira toda a vantagem possível de BLAS: neste exemplo Python/Numpy
é ligeiramente mais rápido que Fortran.
Lemma
UFPR
37. 15
Conclusões, com BLAS
• BLAS faz toda a diferença: não saia de casa sem ele.
• BLAS torna um programa Fortran otimizado para multiplicar matrizes 6 vezes
mais rápido.
• Numpy tira toda a vantagem possível de BLAS: neste exemplo Python/Numpy
é ligeiramente mais rápido que Fortran.
• Com esforço suficiente (opções de compilação, BLAS compilado para a má-
quina, etc.) deve ser possível tornar Fortran mais rápido que Python/Numpy.
Lemma
UFPR
38. 16
III: Rodando em Windows
Por que desenvolver para Windows?
Lemma
UFPR
39. 16
III: Rodando em Windows
Por que desenvolver para Windows?
Porque muitos não têm acesso a Linux.
Lemma
UFPR
40. 16
III: Rodando em Windows
Por que desenvolver para Windows?
Porque muitos não têm acesso a Linux.
Como ter Fortran/BLAS gratuitamente em Windows:
• Obtenha e instale MingGW www.mingw.org
• Dentro do shell de MingGW, obtenha e instale wget: mingw-get install wget
• Obtenha OpenBLAS http://xianyi.github.com/OpenBLAS/
• Desempacote na pasta c:mingwmsys1.0OpenBLAS
• Nesta pasta (cd /OpenBLAS/):
– ./quickbuild.win32
– Espere uma eternidade (muitas horas ou uma noite inteira)
– make install /mingw (deve copiar libopenblas.lib para c:mingwlib)
• Copie libopenblas.dll para alguma pasta no PATH (por exemplo,
c:mingwbin)
Lemma
UFPR
42. 18
Conclusões desta parte
• Não há numba em meu Windows7; portanto, não há comparação com
cacumij.py.
• Os tempos de máquina dependem dos processos rodando em todo o sistema
operacional anfitrião (Linux); portanto, as comparações são aproximadas e de-
pendem do estado do sistema.
• Os tempos em Windows são comparáveis com os tempos em Linux: depen-
dendo do caso, Fortran ou Python podem “ganhar” entre si ou entre sistemas
operacionais diferentes.
Lemma
UFPR
43. 19
Conclusões gerais
• Não é possível ainda prescindir totalmente de Fortran ou C: vale a pena man-
ter compiladores atualizados em todos os sistemas operacionais em que for
necessário trabalhar.
• Fortran sem BLAS não é competitivo com Python/Numpy, todas as vezes em
que operações vetoriais e matriciais forem uma parte significativa (em tempo
de máquina) do programa.
• Portanto, é fundamental possuir BLAS no sistema, tão otimizado quanto possí-
vel.
• Python/Numpy/Numba possuem desempenho comparável a Fortran, pelo me-
nos em ordem de grandeza (digamos, entre 50% e 100% da velocidade de
Fortran).
Lemma
UFPR
44. 19
Conclusões gerais
• Não é possível ainda prescindir totalmente de Fortran ou C: vale a pena man-
ter compiladores atualizados em todos os sistemas operacionais em que for
necessário trabalhar.
• Fortran sem BLAS não é competitivo com Python/Numpy, todas as vezes em
que operações vetoriais e matriciais forem uma parte significativa (em tempo
de máquina) do programa.
• Portanto, é fundamental possuir BLAS no sistema, tão otimizado quanto possí-
vel.
• Python/Numpy/Numba possuem desempenho comparável a Fortran, pelo me-
nos em ordem de grandeza (digamos, entre 50% e 100% da velocidade de
Fortran).
Tenha a sabedoria de escolher as melhores ferramentas.
Lemma
UFPR