Ganhando performance no Python com Numba em projetos não científicos
amesias
146 views
24 slides
Nov 04, 2020
Slide 1 of 24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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.
Size: 5.13 MB
Language: pt
Added: Nov 04, 2020
Slides: 24 pages
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