Prontidão vs. conclusão Async IO Memory use?


12

Eu estava assistindo essa palestra sobre a implementação de Async IO no Rust e Carl menciona dois modelos em potencial. Prontidão e conclusão.

Modelo de prontidão:

  • você diz ao kernel que deseja ler de um soquete
  • faça outras coisas por um tempo…
  • o kernel informa quando o soquete está pronto
  • você lê (preenche um buffer)
  • faça o que precisar
  • libere o buffer (acontece automaticamente com o Rust)

Modelo de conclusão:

  • você aloca um buffer para o kernel preencher
  • faça outras coisas por um tempo…
  • o kernel informa quando o buffer foi preenchido
  • faça o que precisar com os dados
  • liberte o buffer

No exemplo de Carl de usar o modelo de prontidão, você poderia iterar sobre soquetes prontos, preenchendo e liberando um buffer global, o que faz parecer que ele usaria muito menos memória.

Agora minhas suposições:

Sob o capô (no espaço do kernel), quando se diz que um soquete está "pronto", os dados já existem. Ele entrou no soquete pela rede (ou de qualquer lugar) e o sistema operacional está mantendo os dados.

Não é como se essa alocação de memória magicamente não acontecesse no modelo de prontidão. É só que o sistema operacional está abstraindo de você. No modelo de conclusão, o sistema operacional está solicitando que você aloque memória antes que os dados entrem e é óbvio o que está acontecendo.

Aqui está minha versão alterada do modelo de prontidão:

  • você diz ao kernel que deseja ler de um soquete
  • faça outras coisas por um tempo…
  • ALTERAÇÃO: os dados chegam ao sistema operacional (algum lugar na memória do kernel)
  • o kernel informa que o soquete está pronto
  • você lê (preencha outro buffer separado do buffer do kernel acima (ou você recebe um ponteiro para ele?))
  • faça o que precisar
  • libere o buffer (acontece automaticamente com o Rust)

/ Minhas suposições

Por acaso, gosto de manter o programa de espaço do usuário pequeno, mas eu só queria alguns esclarecimentos sobre o que realmente está acontecendo aqui. Não vejo que um modelo utilize inerentemente menos memória ou suporte a um nível mais alto de E / S simultânea. Eu adoraria ouvir pensamentos e explicações mais profundas sobre isso.


Eu também cheguei aqui daquela conversa do YouTube. Para quem está aprendendo sobre como E / S assíncrona ou como implementar loops de eventos, a equipe do Rust tem esta lista de reprodução de "Entrevistas Aysnc" aqui entrevistando pessoas muito informadas da comunidade
cacoder 19/01/19

Respostas:


5

No modelo de prontidão, o consumo de memória é proporcional à quantidade de dados não consumidos pelo aplicativo.

No modelo de conclusão, o consumo de memória é proporcional à quantidade de chamadas de soquete pendentes.

Se houver muitos soquetes ociosos, o modelo de prontidão consumirá menos memória.

Há uma correção fácil para o Modelo de Conclusão: Inicie uma leitura de 1 byte. Isso consome apenas um pequeno buffer. Quando a leitura é concluída, emite outra leitura (talvez síncrona) que obtém o restante dos dados.

Em alguns idiomas, o Modelo de Conclusão é extremamente simples de implementar. Considero que é uma boa opção padrão.


1

No modelo de conclusão, o sistema operacional está solicitando que você aloque memória antes que os dados entrem e é óbvio o que está acontecendo.

Mas o que acontece se houver mais dados do que você alocou espaço? O kernel ainda precisa alocar seu próprio buffer para não descartar os dados. (Por exemplo, é por isso que o truque de leitura de 1 byte mencionado na resposta de usr funciona.)

A desvantagem é que, embora o Modelo de Conclusão consuma mais memória, ele também pode (às vezes) executar menos operações de cópia, porque manter o buffer ao redor significa que o hardware pode fazer DMA diretamente dentro ou fora dele. Também suspeito (mas tenho menos certeza) de que o Modelo de Conclusão tende a executar a operação de cópia real (quando existe) em outro encadeamento, pelo menos para o IOCP do Windows, enquanto o Modelo de Prontidão o faz como parte do processo não-bloqueador read()ou write()ligar.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.