Prontezza vs. completamento Asincrono Utilizzo della memoria IO?


12

Stavo guardando questo discorso sull'implementazione di Async IO in Rust e Carl menziona due potenziali modelli. Prontezza e completamento.

Modello di prontezza:

  • dici al kernel che vuoi leggere da un socket
  • fare altre cose per un po '...
  • il kernel ti dice quando il socket è pronto
  • hai letto (riempi un buffer)
  • fai quello che ti serve
  • libera il buffer (succede automaticamente con Rust)

Modello di completamento:

  • allocare un buffer per il kernel da riempire
  • fare altre cose per un po '...
  • il kernel ti dice quando il buffer è stato riempito
  • fai tutto il necessario con i dati
  • libera il buffer

Nell'esempio di Carl sull'uso del modello di prontezza, è possibile scorrere i socket pronti riempiendo e liberando un buffer globale che fa sembrare che userebbe molta meno memoria.

Ora i miei presupposti:

Sotto il cofano (nello spazio del kernel) quando si dice che un socket è "pronto", i dati esistono già. È entrato nel socket sulla rete (o da qualsiasi luogo) e il sistema operativo sta trattenendo i dati.

Non è come se l'allocazione della memoria magicamente non avvenisse nel modello di prontezza. È solo che il sistema operativo lo sta sottraendo da te. Nel modello di completamento, il sistema operativo ti chiede di allocare memoria prima che i dati entrino effettivamente ed è ovvio che cosa sta accadendo.

Ecco la mia versione modificata del modello Readiness:

  • dici al kernel che vuoi leggere da un socket
  • fare altre cose per un po '...
  • EMENDAMENTO: i dati arrivano al sistema operativo (un posto nella memoria del kernel)
  • il kernel ti dice che il socket è pronto
  • leggi (riempi un altro buffer separato dal buffer del kernel overver (o ottieni un puntatore ad esso?))
  • fai quello che ti serve
  • libera il buffer (succede automaticamente con Rust)

/ I miei presupposti

Mi piace mantenere piccolo il programma spazio utente, ma volevo solo qualche chiarimento su ciò che, in realtà, sta succedendo qui. Non vedo che un modello utilizzerebbe intrinsecamente meno memoria o supporterebbe un livello più elevato di IO simultaneo. Mi piacerebbe sentire pensieri e spiegazioni più profonde di questo.


Sono arrivato anche qui da quel discorso su YouTube. Per chiunque sappia come IO asincrono o come implementare loop di eventi, il team Rust ha questa playlist di "Interviste Aysnc" qui che intervista persone molto informate della comunità
cacoder

Risposte:


5

Nel modello Readiness il consumo di memoria è proporzionale alla quantità di dati non consumata dall'applicazione.

Nel modello di completamento il consumo di memoria è proporzionale alla quantità di chiamate socket in sospeso.

Se ci sono molti socket che sono per lo più inattivi, il modello Readiness consuma meno memoria.

Esiste una soluzione semplice per il modello di completamento: avviare una lettura di 1 byte. Questo consuma solo un piccolo buffer. Una volta completata la lettura, viene emessa un'altra lettura (forse sincrona) che ottiene il resto dei dati.

In alcune lingue il modello di completamento è estremamente semplice da implementare. Ritengo sia una buona scelta predefinita.


1

Nel modello di completamento, il sistema operativo ti chiede di allocare memoria prima che i dati entrino effettivamente ed è ovvio che cosa sta accadendo.

Ma cosa succede se arrivano più dati di quelli a cui hai assegnato lo spazio? Il kernel deve ancora allocare il proprio buffer in modo da non eliminare i dati. (Ad esempio, questo è il motivo per cui il trucco di lettura a 1 byte menzionato nella risposta di usr funziona.)

Il compromesso è che mentre il modello di completamento consuma più memoria, può anche (a volte) fare meno operazioni di copia, perché mantenere il buffer in giro significa che l'hardware può DMA direttamente fuori o dentro di esso. Ho anche il sospetto (ma sono meno sicuro) che il Modello di completamento tende a eseguire l'operazione di copia effettiva (quando esiste) su un altro thread, almeno per lo IOCP di Windows, mentre il Modello di preparazione lo fa come parte del non blocco read()o write()chiamata.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.