O documento discute como as linguagens de programação aprenderam a usar os recursos do sistema operacional de forma mais eficiente através de coroutinas e concorrência. Explica como coroutinas permitem a execução assíncrona de tarefas em uma única thread de forma mais rápida do que threads tradicionais, contornando limitações como o GIL do Python. Também descreve componentes-chave do sistema operacional como processos, troca de contexto e alocação de recursos.
3. Registradores especiais
Os registradores especiais são peças fundamentais.
- PC: program counter - responsável por apontar( endereço de memória) sempre a próxima instrução em
tempo de execução.
- Registrador de instrução - De fato registra a instrução a ser feita no momento.
- RET - responsável por salvar o endereço de execução quando tem-se uma chamada para outra função.
Execução de sub-rotina( desvio para outro endereço de memória) e precisa voltar ao ponto anterior.
- ACC - acumulador, usado para operações de incremento.
obs: A + 1 é mais custoso que A++. A +1 precisa de mais um ciclo de clock para operar, enquanto o A ++
se beneficia do Acc.
4. DMA - Direct Memory Access
- O acesso direto da memória é um ponto importante para entendermos alguns pontos
sobre a coroutine.
- O SO é baseado em interrupções.
- DMA usado por I/O, capaz de transmitir informação em velocidade próxima a de
memória.
- O controlador( responsável por gerenciar I/O) transfere blocos de dados de
armazenamento em buffer diretamente para memória principal, sem interrupção da CPU.
- Interrupção por bloco e não por byte(clock de máquina).
5. Processos do SO
- Basicamente é a execução de um programa.
Tudo aqui que está sendo executado dentro do
computador, se torna um processo.
Um processo ao ser processado dentro da
memória ele tem a seguinte representação:
- Execução de instrução sequencial
- PC especifica a próxima instrução.
6. Descrição dos componentes
Stack - informações temporárias do processo, tais quais métodos/funções,
parâmetros, endereço de retorno e variáveis locais;
Heap - é alocado memória dinamicamente num processo durante o tempo de
execução;
Data - Contém todas as variáveis locais e globais do processo;
Text - Inclui a atividade atual representada pelo program Counter(PC) e o
conteúdo dos registros do processador.
7. Ao ser executado o processo ele pode ter diversos estados:
- Novo: Está sendo criado;
- Executando: Instruções estão sendo executadas;
- Esperando: A espera de algum evento acontecer;
- Pronto: A espera de ser atribuído a um processador;
- Terminado: Terminou a execução.
- O processo utiliza recursos - cpu, memória, I/O
- O processo executa instruções sequencialmente
Estados e características do Processo
8.
9. Alocação de Recursos
- Tarefas necessitam que seja alocado recursos a elas.
- Ciclos de CPU/MP e armazenamento de arquivo podem ter códigos de alocação
especial;
- Outros (I/O) código geral de solicitação e liberação de recurso.
- Scheduler dispatcher: Da o controle da CPU ao processo selecionado pelo
escalonamento de curto prazo.
- Operação envolve troca de contexto.
10. Pipeline
- Pseudo paralelismo de execução.
- Parelelismo necessita de mais UC(núcleos físicos) e mais
processadores(hardware)
-Slots são compartilhados maximizando as instruções que estão sendo
executadas.
- pipeline é de arquitetura RISK. Possibilitou as criação de threads
11. Threads
- ações dentro de um processo de hardware ou SO.
- As threads ocorrem dentro de um processo. Compartilham o espaço de memória usado pelos
mesmo;
- Mais rápidas de criar e destruir, pois não possuem quaisquer recursos associados a eles;
- um processo pode ser dividido em várias threads;
- quando uma thread está em estado bloqueado, o processo como um todo não fica parado;
- aproveitamento de contexto, processamento de CPU e memória;
- podem executar em paralelo dividindo um espaço de memória.
12.
13. Context Switch - Troca de Contexto
- Troca de processo ou troca de task de execução;
- CPU troca um processo ou thread por outro;
- O contexto é conteúdo dos registradores da CPU que são uma
- pequena porção de uma memória
- extremamente rápida dentro da CPU;
- A troca de contexto é essencial para
- o conceito de multitasking.
14. Context Switching - Multitask
- Múltiplos processos executados em uma única CPU aparentemente
simultaneamente e sem interferir um ao outro;
- Ilusão de concorrência - a troca de contexto ocorre muito rápido( dez ou cem
vezes por segundo);
- A troca de contexto ocorre quando cada thread ou processo atinge o time
slice da CPU determinado para cada um deles, evitando que os mesmo
fiquem mais tempo consumindo recurso;
15. Context Switch - Custo
- Tempo de processamento, que pode ser na ordem de nanosegundos para cada uma das dezenas ou
centenas de trocas por segundo;
- Tempo levado pela troca de contexto não é grande em comparação ao tempo absoluto de consumo da
CPU;
- É importante salientar que ter threads ativas em um número maior que as threads de hardware o custo
na troca de um core para outro é grande, pois o custo de cache aumenta;
- threads ativas são threads que tem o seu próprio working set;
- Para o SO(no caso linux) é mais fácil o fazer re-scheduling da mesma thread no core de último uso.
- É usado o swapping para a troca de contexto. Processado temporariamente é trocado entre memória e
um armazenamento de apoio, e depois trazido de volta para execução.
16. Python Async - Generators
- Generators é um dos carros chefes da linguagem;
- Permite que retorne dinamicamente um elemento de uma lista sem ter que retornar toda a lista de uma única vez;
- A palavra Yield representa um generator em python;
- yield permite que a função retorne os valores de uma lista futuramente, não sendo necessário retornar todo os
elementos de uma única vez;
- Todo iterator possui a método __iter__() que é um método reservado no python;
- o yield tem o seu método __next__() que implementar um __iter__();
- __next__ permite passar de volta o valor para quem quer esteja chamando o método next() ;
19. Coroutine - Definição
- Coroutine é uma função que trabalho com estado e para cada estado ela se comporta diferente;
- Seria como uma coleção de múltiplas rotinas que dividem o mesmo código, e são ligadas através do estado de
máquina;
- Coroutine são multitasks subordinas não preemptivas;
- Funcionam sim, como os generators já existente na linguagem;
- Coroutine são como os generators porém diferentes:
—> generators são produtores de dados;
—> coroutines são consumidores de dados;
- Coroutine basicamente, consomem dados on demand.
20. Por que são mais rápidos?
Endereço lógico - gerado pela CPU; também conhecido como endereço lógico
Endereço físico - endereço visto pela unidade de memória
MMU - mapeia o endereço lógico para o endereço físico
Maior o page folder, maior é a lentidão para fazer o swapping e carrega do endereço lógico ao físico.
swapping - ligado a transbordo ( troca de contexto), envolve a carregamento de disco.
Memória auxiliar( armazenamento de apoio) e memória para execução. ( ta na RAM executa, ta no HD
executa o swapping)
First-fit - primeiro espaço
best-fit - alocar o menor tamanho suficiente, varre a lista inteira
worst-fit - aloca o maior buraco(tamanho), varre a lista inteira
21. Versão 2.5
- Coroutine criadas para versões a partir da 2.5.
- Modificações do yield( agora como receiver)
- Todas as coroutines precisam ser inicializadas, seja pela chamada next() ou
send(None) para que seja invocada.
24. Courotine - Python Async
- Estimula concorrência no processo usando um único processo de CPU;
- Async implementa I/O assíncrono com a ajuda das coroutines;
- Coroutines é a generalização das subrotinas;
- Cada invocação dessa “subrotina" tem o mesmo ponto de partida e mesmo
ponto de parada;
25. Asyncio
- Usa single-thread, single-process(concurrent) e há troca de contexto(tasks)
dado a um time slice.
- Essas trocas ocorrem em momento que o programa pararia(block) para ler ou
gravar dados, o asyncio da suporte para o agendamento(future) para permitir que
uma coroutine seja notificado ao termino de uma outra.
- Multiplexação de acesso I/O.
26. Coroutine - Como a magia funciona
- O sistema operacional agenda (schedule) a ação de suas thread de acordo com os
processadores disponíveis;
- A coroutine irá agendar sua execução dentro de um processador lógico que está
alocado dentro de uma única thread;
- Dezenas ou até centenas de coroutines podem ser agendada para correr
concorrentemente;
27. Concorrência vs Paralelismo
- Concorrência não é paralelismo;
- paralelismo é quando uma ou mais thread são executadas simultaneamente em
diferentes processos;
- o paralelismo exige múltiplos processos físicos;
- coroutine irá executar a concorrência dentro de um processador físico;
28. Coroutine - Event Loop
- event loop é uma construção dentro do programa que espera e dispara eventos
ou mensagens dentro de um programa;
- basicamente ele permite que quando um evento A ocorra faça evento B;
- Um exemplo clássico para descrever event loop é onclick do javascript;
- Event loop foi adicionado ao python para dar suporte a eventos assíncronos;
- usado na execução de thread ou subprocesso, ele funciona como um scheduler;
29. Multitask cooperativa
- Conhecida como não preemptiva;
- o sistema operacional nunca inicial a troca de contexto de um processo em execução para
um outro;
- os processos voluntariamente irão ser retornados periodicamente (yield concept)
permitindo que múltiplas aplicações ocorram simultaneamente;
- o SO apenas inicia o processo, e espera que eles retornem o controle a ele
voluntariamente.
30. Coroutines - Awaitables and Futures
- A combinação entre coroutines e async permite que um SO normal se comporte
como um SOTR;
- Na linguagem python alguns objetos são awaitables( como os generators);
- os Futures são objetos que eventualmente irão segurar um valor que poderá ser
usado no código num eventual resultado, enquanto ainda está executando;
- ao chamar o future (asyncio.ensure_future) você permite que os objetos assim que
estiverem prontos sejam utilizados enquanto os outros ainda estão em processo;
- a palavra reservada no python é await;
31. Coroutine - Tasks
- Taks são as futures encapsulada por uma coroutine;
- funciona como future mas contém mais informações:
—> permite extrair a pilha atual que contém a coroutine.
- várias tasks ocorrem dentro de uma thread
- Coroutines (e tasks) só podem executar enquanto o event loop está rodando.
32.
33. Coroutines - Ações
- pode suspender uma coroutine até um evento futuro acabar;
- esperar por outra coroutine para finalizar um resultado;
- produzir o resultado da coroutine que está aguardando está em vigor com a
palavra wait;
- permitindo que no python exista método que criem várias coroutines e uma pode
depender do resultado da outra;
35. Coroutine vs múltiplas threads
- Uma dúvida muito grande é: Por que usar coroutine e não múltiplas thread?
- GIL — Global Interpreter Lock
- O GIL é usado pelo módulo interno CPython para garantir que apenas uma thread execute um python bytecode( um objeto python) por
vez.
- garante que seja thread safe ( acesso concorrente), pois o python não é;
-simultâneamente duas threads tentam incrementar uma referencia em um mesmo objeto python, este incremento pode potencialmente
acontecer de fato apenas uma vez e não duas.
- O GIL vai fazer internamente todo o gerenciamento das threads bem como o sistema operacional executa com os processos, sendo
assim, haverá um timeslice para cada thread ser executada, haverá troca de contexto para cada uma.
-Quando se diz que apenas uma thread é executada por vez, não significa que não possa ser executadas diversas threads, pode, porém
toda vez que haver uma chamada interna ao CPython API functions ou uma referencia acontecer à um objeto, o GIL irá garantir que
apenas uma thread faça isso por vez.
- O que em muitos casos irá onerar bastante na performance do seu código;