Slide 1

Slide 1 text

Python I/O Performance: Asyncio + Uvloop

Slide 2

Slide 2 text

Programa Python

Slide 3

Slide 3 text

Imagine que precisamos fazer um script que faz uma contagem de quantos status tiveram sucesso de uma lista de URLs e devolver o total de status com sucesso em formato JSON; Vamos verificar algumas abordagens. Problema: Healthcheck Counter

Slide 4

Slide 4 text

Single Thread (Sem paralelismo) Fonte: https://www.youtube.com/watch?v=qfY2cqjJMdw

Slide 5

Slide 5 text

Problemas O script levou muito tempo para rodar; É necessário cada requisição ser feita e terminar de receber os dados para ir para a próxima. Vantagens Simplicidade do código

Slide 6

Slide 6 text

Multi processos : Podemos abrir vários processos assim cada requisição rodaria de forma paralela. Multi Processing

Slide 7

Slide 7 text

Fonte: https://www.youtube.com/watch?v=qfY2cqjJMdw

Slide 8

Slide 8 text

Problemas Memória não é compartilhada entre os processos; Precisa separar os dados e depois juntá-los novamente. Vantagens Velocidade na execução do script; Consegue executar mais de uma requisição por vez;

Slide 9

Slide 9 text

Multithreading Fonte: https://www.studytonight.com/operating-system/multithreading

Slide 10

Slide 10 text

“Multithreading é a capacidade que o sistema operacional possui de executar várias threads simultaneamente sem que uma interfira na outra. Estas threads compartilham os recursos do processo, mas são capazes de ser executadas de forma independente” (Fonte: tecmundo)

Slide 11

Slide 11 text

Fonte: https://www.youtube.com/watch?v=qfY2cqjJMdw

Slide 12

Slide 12 text

Problemas Necessário controlar o acesso à variável e execução gerenciando locks/semáforos; Código mais complexo. Vantagens Memória compartilhada; Menos recursos consumidos; Script com execução mais rápida.

Slide 13

Slide 13 text

Perae, mas e o GIL??

Slide 14

Slide 14 text

“The Python Global Interpreter Lock or GIL, in simple words, is a mutex (or a lock) that allows only one thread to hold the control of the Python interpreter. This means that only one thread can be in a state of execution at any point in time. The impact of the GIL isn’t visible to developers who execute single-threaded programs, but it can be a performance bottleneck in CPU-bound and multi-threaded code.”

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Duas referências para Foo Três referências para Foo

Slide 17

Slide 17 text

O Python (CPython) utiliza essa contagem para fazer o gerenciamento de memória (GC); Se houver race condition, pode ocorrer de incrementar/decrementar a contagem em refcount ao mesmo tempo; Ou seja, nunca fazemos uso de múltiplas CPUs de forma simultânea em Python

Slide 18

Slide 18 text

Problema de incrementar indevidamente: memory leak; Problema de decrementar indevidamente: variável eliminada quando ainda há referências à ela.

Slide 19

Slide 19 text

Espera, mas se o GIL permite que somente rode uma Thread por vez, por que tivemos um ganho de velocidade ao abrir processos/threads no script?

Slide 20

Slide 20 text

“Para operações de I/O, o GIL é liberado para outro processo de forma paralela também execute até a primeira chamada retornar resultado” https://drgarcia1986.github.io/blog/2016/02/18/threads-em-python-e-claro/

Slide 21

Slide 21 text

Event Loop

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Vantagens Mais rápido para operações que envolvam I/O em Python; Memória compartilhada; Mais fácil de codificar; Boa abordagem para lidar com protocolos de rede (conexões com socket)

Slide 24

Slide 24 text

Fonte: https://www.youtube.com/watch?v=qfY2cqjJMdw

Slide 25

Slide 25 text

Implementações Event Loop  Twisted Curio gevent    asyncio

Slide 26

Slide 26 text

Asyncio ● Biblioteca nativa Python (3.5>) para escrever código concorrente usando async/await; ● Base para frameworks assíncronos em Python que fornecem alta performance rede, servidores web, banco de dados, filas de tarefas distribuídas etc; ● Evita callback hell utilizando geradores;

Slide 27

Slide 27 text

O que temos no asyncio? ● Event Loop (plugável); ● Interfaces para protocolos e transporte; ● Fábricas para servers e conexões; ● Futures/Tasks: callbacks, corrotinas, timeouts, cancellation ● Subprocessos, filas, mecanismos de sincronização

Slide 28

Slide 28 text

Uvloop 99,9% compatível com event loop; Escrito em Cython (Escrever extensões em C - sintaxe similar ao Python, porém estática); Usa libuv por baixo dos panos: não usa o socket nativo Python (tudo roda em cima da libuv); I/O é mais rápido.

Slide 29

Slide 29 text

Libuv

Slide 30

Slide 30 text

Libuv Async I/O multiplataforma; Desenvolvido em C; Uso nativo em Node;

Slide 31

Slide 31 text

Event Loop (Single Thread); Processos; Timers; Sockets TCP/UDP; Named pipes; Operações em sistema de arquivos (Thread Pool); Signal handling; Processos filhos; Utilitários para Threads O que temos na libuv?

Slide 32

Slide 32 text

“We take care of the sheet. You don’t have to.” IBARRA, Saul

Slide 33

Slide 33 text

Arquitetura do Libuv Event Loop (onde as coisas ocorrem); Handles; Requests; Outros utilitários;

Slide 34

Slide 34 text

Específicos para fazer/lidar com algum tipo de trabalho; Exemplos: Timer (tipo de Handle): chama um callback após um determinado tempo; TCPHandles: lidam com a conexão na escrita/leitura Handles

Slide 35

Slide 35 text

Operações em um handler ou por si só Exemplo: UVWrite é uma requisição para escrever dados em uma conexão stream (usa o StreamHandler) Requests

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Libuv Event Loop Onde as “coisas acontecem”; Single Threaded.

Slide 39

Slide 39 text

Como Event Loop do Libuv trabalha?

Slide 40

Slide 40 text

Atualiza o tempo atual sempre que volta ao início Verifica se o loop deve continuar rodando Verifica os timers que já estão no passado Comparando com o tempo atual. Se sim, executa. Callbacks de operações anteriores que foram completados. Exemplo: Escrever em um TCP Handle e a mesma termina e o Callback com o resultado é devolvido. Block de I/O: novos eventos e novas conexões. Callbacks de leitura de dados.

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

Voltando ao Uvloop...

Slide 43

Slide 43 text

Uvloop Performance

Slide 44

Slide 44 text

Se otimizado corretamente, consegue atingir nível de performance em Go para redes Uvloop Performance

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

Então só usar uvloop já faz minha aplicação ficar super rápida?

Slide 49

Slide 49 text

Não! Depende de COMO libs que utilizam asyncio foram implementadas

Slide 50

Slide 50 text

Formas de implementar libs com asyncio ● Sockets (sock_sendall, sock_recv, sock_connect); ● Streams (StreamReader / StreamWriter); ● Protocolos de baixo nível e Transportes

Slide 51

Slide 51 text

Sockets Maior facilidade de implementação se você já utiliza sockets síncronos; Porém não consegue fazer buffer dos dados Não há controle sobre o fluxo;

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Streams Nível maior de abstração; Tem uma performance melhor que a implementação com sockets, pois event loop sabe mais sobre a aplicação; Facilidade para utilização, mas é muito genérico

Slide 54

Slide 54 text

aiohttp Abstração de streaming customizada sob medida para o protocolo concreto para utilizar async/await

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

Protocolos/Transporte Permite você escrever em Cython,C,Rust, whatever; Melhor opção para performance; Controle total do fluxo I/O (“Event Loop pare de me mandar dados”); Implementação de buffers de leitura/escrita personalizados Callback hell (use Facade);

Slide 57

Slide 57 text

Protocolos x Transporte Transporte: “Como os dados serão transmitidos?” Protocolo: “Quais dados serão transmitidos” Transporte: Abstração para um socket (I/O endpoint); Protocolo: Abstração para uma aplicação; Transporte: Chama o protocolo para enviar dados; Protocolo: Chama o transporte para passar dados que foram recebidos

Slide 58

Slide 58 text

asyncpg

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

Implementação Formato de dados binário ao invés de texto; Nem todos os tipos podem ser utilizados e parseados como texto no Postgres; Escrito em Cython/Python; Cython é utilizado para trabalhar com char para criar buffers em Python, assim evita que o Python aloque e desaloque memória muitas vezes (parse do PostgreSQL protocol).

Slide 61

Slide 61 text

Gabaritando performance asyncio + uvloop

Slide 62

Slide 62 text

● Implemente protocolos/transporte sempre que possível para melhor performance; ● Utilize dados em formato binário (menos dados / melhor parse) sempre que possível; ● Torne público apenas async/await e esconda quaisquer complexidades envolvendo callbacks; ● Use uma linguagem mais “low level”: Cython/C/Rust; Preferência por usar/criar libs que...

Slide 63

Slide 63 text

Script para obter a variação de bitcoins do ano de 2016 API retorna por dia; Logo: 365 requisições

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

Sem Uvloop

Slide 66

Slide 66 text

Com uvloop

Slide 67

Slide 67 text

Dúvidas/Sugestões?