Sou apaixonado pelo meu interesse no desenvolvimento de software, especificamente pelo quebra -cabeça de criar sistemas de software ergonomicamente que resolvem o conjunto mais amplo de problemas, tornando o mínimo possível de comprometer. Também gosto de pensar em mim como desenvolvedor de sistemas, que, pela definição de Andrew Kelley, significa um desenvolvedor interessado em entender completamente os sistemas com os quais estão trabalhando. Neste blog, compartilho com você minhas idéias sobre como resolver o seguinte problema: Construindo um aplicativo corporativo de pilha completa confiável e performente . Um grande desafio, não é? No blog, concentro -me na parte "Performant Web Server" - é aí que sinto que posso oferecer uma nova perspectiva, pois o resto é bem tridado, ou não tenho nada a acrescentar.
Uma ressalva principal - haverá sem amostras de código , eu realmente não testei isso. Sim, essa é uma falha importante, mas na verdade implementando isso levaria muito tempo, o que eu não tenho, e entre publicar um blog falho e não publicá -lo, fiquei com o primeiro. Você foi avisado.
e que peças montaríamos nosso aplicativo?
Os coroutines são superestimados de qualquer maneira?
Hold, on, vamos primeiro colocar nossos sistemas programador de chapéu. Coroutines não são uma bala de prata, nada é. Quais são os benefícios e desvantagens reais envolvidos?
é o conhecimento comum de que os coroutines (threads de espaço de usuários) são mais light e mais rápido. Mas de que maneira exatamente? (As respostas aqui são em grande parte especulações, tomam com um grão de sal e testem você mesmo)
eles começam com menos espaço de pilha por padrão (2kb em vez de 4 MB). Mas isso pode ser ajustado manualmente.
o tempo de execução Go, por exemplo, multiplexes goroutines nos threads do sistema operacional. Os threads compartilham a tabela de páginas, bem como outros recursos pertencentes a um processo. Se introduzirmos o isolamento e a afinidade da CPU com a mistura - os threads funcionarão continuamente em seus respectivos núcleos de CPU, todas as estruturas de dados do sistema operacional permanecerão na memória sem necessidade de ser trocadas, o agendador do Usuário alocará tempo de CPU para goroutines com precisão, porque ele usa o modelo multitações cooperativas. A competição é possível?
As vitórias no desempenho são alcançadas marginando a abstração no nível do OS de um thread e substituindo-o pelo de uma goroutina. Mas nada está perdido na tradução?
Podemos cooperar com o kernel?
agora por que isso é importante? Cada unidade de execução tem suas próprias demandas por recursos do sistema. Cada tarefa complexa pode ser dividida em unidades, onde cada uma pode fazer sua própria solicitação de recursos - memória e tempo da CPU. E quanto mais adiante a árvore das subtarefas você vai, em direção a uma tarefa mais geral - o gráfico de recursos do sistema forma uma curva de sino com caudas longas. E é sua responsabilidade garantir que as caudas não invadam o limite de recursos do sistema. Mas como isso é feito, e o que acontece se esse limite for de fato invadido?
Se usarmos o modelo de um único processo e muitas coroutinas para tarefas independentes, quando uma coroutina ultrapassa o limite de memória - porque o uso da memória é rastreado no nível do processo, todo o processo é morto. Isso é no melhor caso - se você utilizar os cgroups (que é automaticamente o caso de pods em Kubernetes, que possuem um cgroup por vagem) - todo o cgroup é morto. Fazer um sistema confiável precisa que isso seja levado em consideração. E quanto à hora da CPU? Se nosso serviço for atingido com muitas solicitações intensivas em computação ao mesmo tempo, ele não responderá. Em seguida, prazos, cancelamentos, tentativas, reinicializações seguem.
A única maneira realista de lidar com esses cenários para a maioria das pilhas de software convencional está deixando "gordura" no sistema - alguns recursos não utilizados para a cauda da curva - e limitando o número de solicitações simultâneas - que, novamente, levam a recursos não utilizados. E mesmo com isso, vamos matar ou não responder de vez em quando - incluindo solicitações "inocentes" que estão no mesmo processo que o Outlier. Esse compromisso é aceitável para muitos e serve sistemas de software na prática o suficiente. Mas podemos fazer melhor?
Um modelo de simultaneidade
Quanto mais passamos da abstração limpa de processos de curta duração, quanto mais trabalho no nível do sistema operacional precisaríamos cuidar de nós mesmos. Mas também há benefícios a serem obtidos - como fazer uso de io_uring para o Batching IO entre muitos threads de execução. De fato, se uma grande tarefa for composta de subjugas - nós realmente nos preocupamos com a utilização individual de recursos? Apenas para perfis. Mas se, para a grande tarefa, pudéssemos gerenciar (cortar) as caudas da curva de campainha de recursos, isso seria bom o suficiente. Assim, poderíamos gerar tantos processos quanto os pedidos que desejamos lidar simultaneamente, fazer com que eles tenham vida longa e simplesmente reajuste o Ulimit por cada nova solicitação. Portanto, quando uma solicitação ultrapassa suas restrições de recursos, ele recebe um sinal do sistema operacional e é capaz de rescindir graciosamente, não afetando outras solicitações. Ou, se o alto uso de recursos for intencional, poderíamos pedir ao cliente que pague por uma cota de recursos mais alta. Parece muito bom para mim.
Mas o desempenho ainda sofrerá, em comparação com uma abordagem coroutine por solicitação. Primeiro, copiar a tabela de memória de processo é caro. Como a tabela contém referências às páginas de memória, poderíamos fazer uso de grandes páginas, limitando assim o tamanho dos dados a serem copiados. Isso só é diretamente possível com idiomas de baixo nível, como o ZIG. Além disso, a multitarefa no nível do sistema operacional é preventiva e não é cooperativa, o que sempre será menos eficiente. Ou é?
Multitarefa cooperativa com Linux
Uma limitação é o fato de que um thread sched_deadline não pode gastar. Isso nos deixa com dois modelos de simultaneidade - um processo por solicitação, que define o prazo para si e executa um loop de eventos para IO eficiente, ou um processo que, desde o início, gera um tópico para cada micro -tarefa, cada um dos quais define seu próprio prazo e faz uso de filas para comunicação uma. O primeiro é mais reto, mas requer um loop de eventos no espaço do usuário, o último faz mais uso do kernel.
Ambas as estratégias alcançam o mesmo fim que o modelo Coroutine -
cooperando com o kernel, é possível fazer com que as tarefas de aplicativos sejam executadas com interrupções mínimas. Python como uma linguagem de script incorporada
python, por outro lado, é um dos idiomas mais dinâmicos que existem. Aulas, objetos - são todos dicionários sob o capô e podem ser manipulados em tempo de execução da maneira que você quiser. Isso tem uma penalidade de desempenho, mas torna a modelagem dos negócios com aulas e objetos e muitos truques inteligentes práticos. O ZIG é o oposto disso - existem intencionalmente poucos truques inteligentes no ZIG, oferecendo controle máximo. Podemos combinar seus poderes com eles interoperados?
de fato, podemos, devido a ter suporte o C ABI. Podemos fazer com que o intérprete Python seja executado dentro do processo em zig, e não como um processo separado, reduzindo a sobrecarga no custo de tempo de execução e no código de cola. Isso nos permite fazer uso dos alocadores personalizados do ZIG no Python - definindo uma arena para processar a solicitação individual, reduzindo assim, se não eliminando a sobrecarga de um coletor de lixo e definir uma tampa de memória. Uma grande limitação seria os fios de desova do tempo de execução do CPYTHON para a coleção de lixo e a IO, mas não encontrei evidências de que ela o faça. Poderíamos conectar o Python em um loop de eventos personalizados em Zig, com rastreamento de memória por coro-cor-rotina, usando o campo "Contexto" no AbstractMemoryloop. As possibilidades são ilimitadas.
Conclusão
Leitura adicional
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3