Ganhando performance no Python com Numba em projetos não científicos

amesias 146 views 24 slides Nov 04, 2020
Slide 1
Slide 1 of 24
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24

About This Presentation

- Introdução ao Numba, motivação, história, utilidades.
- Instalação e integração da biblioteca em scripts python.
- Exemplo prático de otimização matemática em script existente. - Perguntas e respostas.


Slide Content

Alejandro Mesias @ meszias Campineiro Pai de 3 filhos Zup Innovation Pythonista a +10 anos . Tkinter , Django, Flask, Pymodbus ...

Introdução ao Z umba - 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

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

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

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

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

Exemplo Básico Sequencia de F ibonacci 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 = , 1 for _ in range ( , n- 1 ): c = a + b a = b b = c return a

Exemplo Básico Sequencia de F ibonacci 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 = , 1 for _ in range ( , n- 1 ): c = a+b a = b b = c return a

Exemplo Básico Sequencia de F ibonacci não recursiva. Até 92, 1.000.000 execuções. Usando time.perf_counter_ns () Sem otimização: 4.19s Otimizado: 0.27s ( 1 9.5 x mais rápido ) “... até 200x (comparado com Python puro)”

Exemplo Básico Sequencia de F ibonacci 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 = for i in range (runs): val1 = fibonacci ( fibn ) return val1

Numba - Instalação pip install numba c onda install numba O que será instalado: Numba – 0.51.2 => Lib Numpy – 1.19.4 => Tudo é ndarray llvmlite - 0.34.0 => Compilação

Dicas over exemplos

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 !

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

Numba - utilização Algumas regras de utilização: Evite dict , list , tuplas . Existem recomendações para esses tipos: Typed Dict k ey_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

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 )

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

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)

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 :

Numpy , processamento paralelo, otimização

Calculando distribuição 50:50 de 2 colunas randomicas def calculate3 (x): a = b = c = d = for i in prange ( x.shape [ ]): row = x[i] if row [ ] > 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 = for i in prange ( x.shape [ ]): row = x[i] if row [ ] > 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 = for i in prange(x.shape[ ]): row = x[i] if row[ ] > 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

Comparação de resultados # size_array = 10**6 size_array = 10 ** 9 ttt1 = time.perf_counter_ns() x1 = random.randint( 100 , size =(size_array, 2 )) total_pos1 = calculate1(x1) ttt1= time.perf_counter_ns() - ttt1 x1 = None del x1 print ( f'Fim 1 Tempo: { ttt1 : , } us ( { ttt1/ 10 ** 9 } s)' ) Iterações Calc1 Calc2 Calc3 10**3 0.049s 0.002s 0.0007s 10**6 0.7s 0.12s 0.68s 10**8 2.29s (34x) 1.68 (48x) 78s 10**9 17.72s 17.54s 778s

Fim ...poderia ter falado de Cuda e processamento por GPU Mas nem cheguei a executar, não tenho Nvidia, então ficará para próxima. Perguntas?