O documento resume três pontos principais sobre o Numba: (1) Ele fornece um compilador JIT para Python que equilibra produtividade e desempenho; (2) Permite compilar funções para execução em CPU ou GPU de forma paralela; (3) Testes mostraram que o Numba pode acelerar código em até 200 vezes em relação a Python puro.
Snippy - Rapid bacterial variant calling - UK - tue 5 may 2015Torsten Seemann
Using Snippy to call variants in bacterial short read datasets via alignment to reference, and then using these alignments to produce core SNP alignments for phylogenomics.
Apresentação trilha python do The Developers Conference 2016 em Florianópolis.
Uma introdução à biblioteca Numba (http://numba.pydata.org/) mostrando os prós e contras da biblioteca.
Snippy - Rapid bacterial variant calling - UK - tue 5 may 2015Torsten Seemann
Using Snippy to call variants in bacterial short read datasets via alignment to reference, and then using these alignments to produce core SNP alignments for phylogenomics.
Apresentação trilha python do The Developers Conference 2016 em Florianópolis.
Uma introdução à biblioteca Numba (http://numba.pydata.org/) mostrando os prós e contras da biblioteca.
2016/01/27 - Aprendendo a programar com PythonJardel Weyrich
Apresentação utilizada no treinamento com duração de 10 horas para um grupo de colaboradores da Teltec Solutions - http://teltecsolutions.com.br.
O material de apoio contém exemplos, exercícios e soluções, e pode ser encontrado em https://github.com/jweyrich/aprendendo-a-programar-com-python
2016/01/27 - Aprendendo a programar com PythonJardel Weyrich
Apresentação utilizada no treinamento com duração de 10 horas para um grupo de colaboradores da Teltec Solutions - http://teltecsolutions.com.br.
O material de apoio contém exemplos, exercícios e soluções, e pode ser encontrado em https://github.com/jweyrich/aprendendo-a-programar-com-python
Ganhando performance no Python com Numba em projetos não científicos
1. Alejandro Mesias @meszias
• Campineiro
• Pai de 3 filhos
• Zup Innovation
• Pythonista a +10 anos.
• Tkinter, Django, Flask, Pymodbus...
2. Introdução ao Zumba - Contexto
• Python se tornou uma linguagem muito popular de computação
cientifica.
• Python integra bem com bibliotecas compiladas: MKL (intel),
TensorFlow, ROOT (Análise matemática), etc.
• Compilação de modulos em C/C++, ctypes, libs próprias em binário.
*Stan Seibert, Anaconda
3. Introdução ao Numba – Motivação
A Compiler for Python?
Striking a Balance Between Productivity
and Performance.
(Atingindo um equilíbrio entre produtividade e desempenho.)
*Stan Seibert, Anaconda
4. Introdução ao Numba - Motivação
O objetivo foi fazer um compilador que atenda:
• Funcione no interpretador python padrão, e não substituir o mesmo
• Que se integre perfeitamente com Numpy
• Compativel com paradgmas de computação multitread e distribuída
• Pode ser direcionado a non-CPU hardware (fazer uso de GPU/Cuda)
*Stan Seibert, Anaconda
5. Numba: Um compilador JIT Compiler para Python
• Uma biblioteca de compilação “uma função por vez” para python
• Um toolbox de compilação com diferentes objetivos e modelos de execução:
• Single-tread CPU, multi-threaded CPU, GPU
• Funções regulares, funções de array, etc.
• Speed-up: 2x comparado a Numpy, até 200x (comparado com Python puro)
• Combinar facilidade de escrever Python com a velocidades próximas do Fortran.
• O objetivo é capacitar cientistas que criam ferramentas para outros cientistas.
*Stan Seibert, Anaconda
6. Um labirinto sinuoso de trade-offs
Numba pode ser melhor entendido pelo que ele Não é:
• Substituição do interpretador Python: PyPy, Pyston, Pyjion
• Dificil de implementar
• Dificil (mas não impossível) de manter compatibilidade
com extensões python existentes
• Não endereça para destinos não-CPU
• Tradutor do python para C/C++: Cython, Pythran, Theano, ShedSkin, Niutka
• Análise estática de linguagens dinâmicas é limitante
• Código gerado antecipado é subespecializado (tanto em tipos de dados quanto em recursos
de CPU) ou inchado para cobrir todas as variantes.
• Compilação com JIT requer compilador C/C++ no sistema do usuário
7. Exemplo Básico
Sequencia de Fibonacci não recursiva.
Até 92, 1.000.000 execuções.
Usando time.perf_counter_ns()
4315505000µs ou 4.31s
def fibonacci(n):
a, b = 0, 1
for _ in range(0, n-1):
c = a + b
a = b
b = c
return a
8. Exemplo Básico
Sequencia de Fibonacci não recursiva.
Até 92, 1.000.000 execuções.
Usando time.perf_counter_ns()
Sem otimização: 4.19s
Otimizado: 0.22s (19.5x mais rápido)
@njit
def fibonacci(n):
a, b = 0, 1
for _ in range(0, n-1):
c = a+b
a = b
b = c
return a
9. Exemplo Básico
Sequencia de Fibonacci não recursiva.
Até 92, 1.000.000 execuções.
Usando time.perf_counter_ns()
Sem otimização: 4.19s
Otimizado: 0.27s (19.5x mais rápido)
“... até 200x
(comparado
com Python
puro)”
10. Exemplo Básico
Sequencia de Fibonacci não recursiva.
Até 92, 1.000.000 execuções.
Usando time.perf_counter_ns()
Sem otimização: 4.3s
Otimizado: 0.22s (19.5x mais rápido)
Loop Otimizado: 0.042s (101x mais rápido)
@numba.njit("uint64(uint64, uint64)")
def runner(runs, fibn):
val1 = 0
for i in range(runs):
val1 = fibonacci(fibn)
return val1
13. Pontos fracos do Numba
• Documentação com poucos detalhes (nem todos são especialistas!)
• Stack de falha pouco detalhada
• Nem sempre são explícitos os erros de execução. (ex: index no array)
Opcional (NUMBA_DEVELOPER_MODE=1)
• Voltar ao python puro é sempre uma boa opção (NUMBA_DISABLE_JIT=1)
• Use sua intuição !
14. Tipos de variável
Type name(s) Shorthand Comments
intc – C int-sized integer
uintc –
C int-sized unsigned
integer
intp – pointer-sized integer
uintp –
pointer-sized
unsigned integer
float32 f4
single-precision
floating-point
number
float64, double f8
double-precision
floating-point
number
complex64 c8
single-precision
complex number
complex128 c16
double-precision
complex number
Type name(s) Shorthand Comments
Boolean b1
represented as a
byte
uint8, byte u1 8-bit unsigned byte
uint16 u2
16-bit unsigned
integer
uint32 u4
32-bit unsigned
integer
uint64 u8
64-bit unsigned
integer
int8, char i1 8-bit signed byte
int16 i2 16-bit signed integer
int32 i4 32-bit signed integer
int64 i8 64-bit signed integer
15. Numba - utilização
Algumas regras de utilização:
• Evite dict, list, tuplas. Existem recomendações para esses tipos:
• Typed Dict
• key_type
• value_type
from numba.typed import Dict
from numba.core import types
from numpy as np
# The Dict.empty() constructs a typed dictionary.
# The key and value typed must be explicitly declared.
d = Dict.empty(
key_type=types.unicode_type,
value_type=types.float64[:],
)
d['posx'] = np.asarray([1, 0.5, 2], dtype='f8')
String
Lista de float64
ndarray
16. Numba - utilização
Algumas regras de utilização:
• Typed List
• Empty_list
• pega o primeiro tipo usado
from numba import types
from numba.typed import List
alloc_list = List.empty_list(types.int64,
10)
mylist = List()
mylist.append(100)
17. Numba - utilização
Algumas regras de utilização:
• Manupulação de String dentro do numba é limitado
• Não há fstring, string format ou “%s %d” %(‘a’, 1)
• É possível concatenar somando strings
• É possível usar selectors “bom dia”[4:]
• Evite acesso a variáveis globais, a não ser que sejam do numba,
prefira passar por parâmetro (cuidado com o gil).
• Existem classes, mas não funcionam como nopython=true
18. Numba - utilização
Algumas regras de utilização:
• O tipo de cada atributo é
especificado (obrigatório)
• Funciona como uma classe mas,
tem peso extra para compilar
dinamicamente (não tem cache=True)
• Prático para trocar variáveis com
funções numba
spec = [
('value', int32), # a simple scalar field
('array', float32[:]), # an array field
]
@jitclass(spec)
class Bag(object):
def __init__(self, value):
self.value = value
self.array = np.zeros(value, dtype=np.float32)
@property
def size(self):
return self.array.size
n = 21
mybag = Bag(n)
19. Numba - utilização
Algumas regras de utilização:
• Conflito com tipos numpy no @jitclass:
No spec = [ ] apenas tipos
do Numba.
Alternativas
para extrair tipos numpy:
21. Calculando distribuição 50:50 de 2 colunas randomicas
def calculate3(x):
a = b = c = d = 0
for i in prange(x.shape[0]):
row = x[i]
if row[0] > 50:
a += 1
else:
b += 1
if row[1] < 50:
c += 1
else:
d += 1
return [a, b, c, d]
@njit(cache=True)
def calculate2(x):
a = b = c = d = 0
for i in prange(x.shape[0]):
row = x[i]
if row[0] > 50:
a += 1
else:
b += 1
if row[1] < 50:
c += 1
else:
d += 1
return [a, b, c, d]
@njit(parallel=True, nogil=True,
cache=True,
locals={'a': uint64, 'b': uint64,
'c': uint64,'d': uint64})
def calculate1(x):
a = b = c = d = 0
for i in prange(x.shape[0]):
row = x[i]
if row[0] > 50:
a += 1
else:
b += 1
if row[1] < 50:
c += 1
else:
d += 1
return [a, b, c, d]
Python puro Njit Simples Njit paranelizado