Condividi tramite


Latenza di rete e velocità effettiva

Tre problemi principali riguardano l'uso ottimale della rete:

  • Latenza di rete
  • Saturazione della rete
  • Implicazioni per l'elaborazione dei pacchetti

Questa sezione presenta un'attività di programmazione che richiede l'uso di RPC, quindi progetta due soluzioni: una scritta in modo non corretto e una ben scritta. Entrambe le soluzioni vengono quindi esaminate e vengono discusse le relative ripercussioni sulle prestazioni di rete.

Prima di discutere delle due soluzioni, le sezioni successive illustrano e illustrano i problemi di prestazioni correlati alla rete.

Latenza di rete

La larghezza di banda di rete e la latenza di rete sono termini separati. Le reti con larghezza di banda elevata non garantiscono bassa latenza. Ad esempio, un percorso di rete che attraversa un collegamento satellite ha spesso una latenza elevata, anche se la velocità effettiva è molto elevata. Non è raro che un round trip di rete attraversa un collegamento satellite abbia cinque o più secondi di latenza. L'implicazione di tale ritardo è questa: un'applicazione progettata per inviare una richiesta, attendere una risposta, inviare un'altra richiesta, attendere un'altra risposta e così via, attenderà almeno cinque secondi per ogni scambio di pacchetti, indipendentemente dalla velocità con cui il server è. Nonostante l'aumento della velocità dei computer, le trasmissioni satellitari e i supporti di rete si basano sulla velocità della luce, che in genere rimane costante. Di conseguenza, è improbabile che si verifichino miglioramenti della latenza per le reti satellite esistenti.

Saturazione rete

Alcune saturazione si verificano in molte reti. Le reti più semplici da saturare sono collegamenti modem lenti, ad esempio modem analogici standard 56k. Tuttavia, anche i collegamenti Ethernet con molti computer in un singolo segmento possono diventare saturi. Lo stesso vale per le reti wide area con una larghezza di banda ridotta o un collegamento altrimenti sovraccarico, ad esempio un router o un commutatore in grado di gestire una quantità limitata di traffico. In questi casi, se la rete invia più pacchetti rispetto al collegamento più debole può gestire, elimina i pacchetti. Per evitare la congestione dello stack TCP di Windows si riduce quando vengono rilevati pacchetti eliminati, il che può comportare ritardi significativi.

Implicazioni per l'elaborazione dei pacchetti

Quando i programmi vengono sviluppati per ambienti di livello superiore come RPC, COM e anche Windows Socket, gli sviluppatori tendono a dimenticare il lavoro svolto dietro le quinte per ogni pacchetto inviato o ricevuto. Quando arriva un pacchetto dalla rete, un interrupt dalla scheda di rete viene utilizzato dal computer. Viene quindi accodato un DPC (Deferred Procedure Call) e deve passare attraverso i driver. Se viene usata una forma di sicurezza, il pacchetto potrebbe essere necessario decrittografare o verificare l'hash crittografico. A ogni stato devono essere eseguiti anche diversi controlli di validità. Solo successivamente il pacchetto arriva alla destinazione finale: il codice del server. L'invio di molti piccoli blocchi di dati comporta un sovraccarico di elaborazione dei pacchetti per ogni piccolo blocco di dati. L'invio di un grande blocco di dati tende a consumare molto meno tempo cpu in tutto il sistema, anche se il costo dell'esecuzione per molti blocchi di piccole dimensioni rispetto a un blocco di grandi dimensioni può essere lo stesso per l'applicazione server.

Esempio 1: un server RPC progettato in modo non corretto

Si supponga che un'applicazione che deve accedere ai file remoti e che l'attività a portata di mano consiste nel progettare un'interfaccia RPC per modificare il file remoto. La soluzione più semplice consiste nel mirroring delle routine dei file di studio per i file locali. In questo modo può verificarsi un'interfaccia pulita e familiare in modo ingannevole. Ecco un file con estensione idl abbreviato:

typedef [context_handle] void *remote_file;
... .
interface remote_file
{
    remote_file remote_fopen(file_name);
    void remote_fclose(remote_file ...);
    size_t remote_fread(void *, size_t, size_t, remote_file ...);
    size_t remote_fwrite(const void *, size_t, size_t, remote_file ...);
    size_t remote_fseek(remote_file ..., long, int);
}

Questo sembra abbastanza elegante, ma in realtà, questa è una ricetta onorare il tempo per il disastro delle prestazioni. Contrariamente all'opinione comune, la chiamata di procedura remota non è semplicemente una chiamata di procedura locale con un filo tra il chiamante e il chiamato.

Per vedere come questa ricetta masterizza le prestazioni, considerare un file 2K, in cui vengono letti 20 byte dall'inizio e quindi 20 byte dalla fine e vedere come funziona. Sul lato client vengono effettuate le chiamate seguenti (molti percorsi di codice vengono omessi per brevità):

rfp = remote_fopen("c:\\sample.txt");
remote_read(...);
remote_fseek(...);
remote_read(...);
remote_fclose(rfp);

Si supponga ora che il server sia separato dal client da un collegamento satellite con un tempo di round trip di cinque secondi. Ognuna di queste chiamate deve attendere una risposta prima di poter procedere, il che significa un minimo assoluto per l'esecuzione di questa sequenza di 25 secondi. Considerando che stiamo recuperando solo 40 byte, si tratta di prestazioni scandalosamente lente. I clienti di questa applicazione sarebbero furiosa.

Si supponga ora che la rete sia satura, perché la capacità di un router in un punto qualsiasi del percorso di rete viene sovraccaricata. Questa progettazione impone al router di gestire almeno 10 pacchetti se non si dispone di sicurezza (uno per ogni richiesta e uno per ogni risposta). Anche questo non è buono.

Questa progettazione impone inoltre al server di ricevere cinque pacchetti e inviare cinque pacchetti. Anche in questo caso, non è un'implementazione molto buona.

Esempio 2: un server RPC progettato meglio

Verrà ora riprogettata l'interfaccia illustrata nell'esempio 1 e si vedrà se è possibile migliorarla. È importante notare che per rendere questo server davvero buono richiede una conoscenza del modello di utilizzo per i file specificati: tale conoscenza non si presuppone per questo esempio. Pertanto, si tratta di un server RPC progettato meglio, ma non di un server RPC progettato in modo ottimale.

L'idea di questo esempio consiste nel comprimere il maggior numero possibile di operazioni remote in un'unica operazione. Il primo tentativo è il seguente:

typedef [context_handle] void *remote_file;
typedef struct
{
    long position;
    int origin;
} remote_seek_instruction;
... .
interface remote_file
{
    remote_fread(file_name, void *, size_t, size_t, [in, out] remote_file ..., BOOL CloseWhenDone, remote_seek_instruction *...);
    size_t remote_fwrite(file_name, const void *, size_t, size_t, [in, out] remote_file ..., BOOL CloseWhenDone, remote_seek_instruction *...);
}

In questo esempio tutte le operazioni vengono compresse in una lettura e scrittura, che consente un'apertura facoltativa nella stessa operazione, nonché una chiusura e una ricerca facoltative.

Questa stessa sequenza di operazione, se scritta in forma abbreviata, ha un aspetto simile al seguente:

remote_read("c:\\sample.txt", ..., &rfp, FALSE, NULL);
remote_read(NULL, ..., &rfp, TRUE, seek_to_20_bytes_before_end);

Quando si considera il server RPC più progettato, nella seconda chiamata il server verifica che il file_name sia NULL e usi il file aperto archiviato in rfp. Vengono quindi visualizzate le istruzioni di ricerca e il puntatore del file verrà posizionato 20 byte prima della fine prima della lettura. Al termine, riconoscerà che il flag CloseWhenDone è impostato su TRUE e chiuderà il file e chiuderà rfp.

Nella rete ad alta latenza questa versione migliore richiede 10 secondi per il completamento (2,5 volte più veloce) e richiede l'elaborazione di soli quattro pacchetti; due ricevono dal server e due invii dal server. Gli if aggiuntivi e l'annullamento delmarshaling eseguite dal server sono trascurabili rispetto a tutto il resto.

Se l'ordinamento causale viene specificato correttamente, l'interfaccia può anche essere eseguita in modo asincrono e le due chiamate possono essere inviate in parallelo. Quando vengono usate chiamate di ordinamento causale, le chiamate vengono comunque inviate in ordine, il che significa che nella rete ad alta latenza viene subito un ritardo di cinque secondi, anche se il numero di pacchetti inviati e ricevuti è lo stesso.

È possibile comprimere ulteriormente questo elemento creando un metodo che accetta una matrice di strutture, ogni membro della matrice che descrive una determinata operazione di file; una variante remota di I/O a dispersione/raccolta. L'approccio si paga finché il risultato di ogni operazione non richiede ulteriori elaborazioni sul client; in altre parole, l'applicazione leggerà i 20 byte alla fine indipendentemente dai primi 20 byte letti.

Tuttavia, se è necessario eseguire alcune elaborazioni sui primi 20 byte dopo la lettura per determinare l'operazione successiva, comprimere tutto in un'operazione non funziona (almeno non in tutti i casi). L'eleganza di RPC è che un'applicazione può avere entrambi i metodi nell'interfaccia e chiamare entrambi i metodi a seconda delle esigenze.

In generale, quando la rete è coinvolta è preferibile combinare il maggior numero di chiamate in una singola chiamata possibile. Se un'applicazione ha due attività indipendenti, usare le operazioni asincrone e consentire l'esecuzione in parallelo. Essenzialmente, mantenere piena la pipeline.