Test di carico: come generare richieste al secondo?


14

Ho un componente server che funziona su Zeroc-ICE. Quando volevo caricarlo testarlo, ho pensato che usare la libreria parallela per creare più richieste lo avrebbe fatto. Ma finirà così. L'uso della libreria Parallel (Parallel.For) di C # apparentemente è stato più semplice, ma non sembra generare esattamente tutto in parallelo nello stesso istante. Quindi non può essere la definizione per la creazione di N richieste al secondo. Come dovrei farlo? Immagino che chiunque voglia fare prima i test di carico potrebbe davvero pensarci.

  1. Qual è il modo efficace per creare effettivamente N richieste in davvero al secondo?

  2. Un altro mito riguarda la programmazione parallela. Per favore illuminaci se hai usato schemi di programmazione paralleli in C # o .Net in generale. Immagina di avere 5 processi. Come inizieranno tutti e cinque i processi contemporaneamente. Cosa significa il mio consumo di risorse? Ho provato a leggere molti dei materiali disponibili in rete, ma ricevo sempre più domande di quelle che sono la risposta alle mie domande.

  3. Ho usato Parallel.For e ho creato N thread e misurato il tempo. Quindi ho provato la stessa cosa usando Task.Factory.start per l'enumerazione delle attività. Il tempo misurato era diverso. Allora, qual è esattamente la differenza tra l'utilizzo di questi? Quando dovrei usare le classi corrispondenti e per quali scopi esattamente? spesso abbiamo molte ricchezze ma è proprio non sappiamo esattamente come differenziare l'una dall'altra. Questo è uno di questi casi per me, non riuscire a scoprire perché non dovrei usare l'uno dall'altro.

  4. Ho usato la classe cronometro per misurare questi tempi che afferma di essere il migliore. Nello scenario in cui carico test di un componente, quale sarebbe il modo di misurare il tempo di risposta. Il cronometro sembra essere la soluzione migliore per me. Qualsiasi opinione è benvenuta

ps: ci sono molti strumenti di test del carico per le applicazioni web. Il mio è un caso personalizzato di componenti server. E la mia domanda riguarda più la creazione di N thread al secondo.

Tutte le opinioni sono benvenute Non pensare che non sia una domanda di programmazione. Ovviamente lo è. Dovrebbe suonare il campanello per ogni programmatore che vuole fare cose QE da solo per conoscere le prestazioni del suo prodotto, in prima persona da solo. Ho provato molte opzioni e poi ho dovuto ricorrere a come dovrei effettivamente farlo?


La domanda fa riferimento se si tratta di un problema di programmazione specifico e se si tratta di un problema pratico e responsabile nella professione di programmatore, può essere chiesto. le persone che sono scettiche e segnalano questo. per favore, commenta.
Re

Cosa intendi con "lo stesso istante"? Mi chiedo se puoi forzare TPL o PLinq in qualche modo per raggiungere questo obiettivo.
Gert Arnold,

La mia domanda riguarda la generazione di N richieste al secondo. Quindi lo stesso istante in questo scenario era destinato alla mia comprensione dell'uso del parallelo che avrebbe iniziato le discussioni in modo parallelo.
Re

Hai fatto qualche analisi sequenziale?

3
Potrebbe riguardare la programmazione, ma ci sono troppe domande nel tuo post (almeno 4). Lo ridurrei all'unica domanda che vuoi porre prima che si chiuda perché è troppo ampia. Fornisci informazioni pertinenti, come il 10000 che hai appena menzionato, il numero di core nella tua macchina di prova). Mostrare il codice di solito aiuta.
Gert Arnold,

Risposte:


10

Non ho tutte le risposte. Spero di poter far un po 'di luce su di esso.

Per semplificare le mie precedenti dichiarazioni sui modelli di threading di .NET, sappi solo che Parallel Library utilizza Tasks e TaskScheduler predefinito per Tasks utilizza ThreadPool. Più in alto vai nella gerarchia (ThreadPool è in fondo), maggiore è il sovraccarico che hai durante la creazione degli articoli. Quel sovraccarico extra certamente non significa che è più lento, ma è bello sapere che è lì. In definitiva, le prestazioni del tuo algoritmo in un ambiente multi-thread si riducono al suo design. Ciò che funziona bene in sequenza potrebbe non funzionare altrettanto bene in parallelo. Ci sono troppi fattori coinvolti per darti regole dure e veloci, cambiano a seconda di ciò che stai cercando di fare. Dato che hai a che fare con le richieste di rete, proverò a fare un piccolo esempio.

Consentitemi di affermare che non sono un esperto di prese e che non so quasi nulla di Zeroc-Ice. So qualcosa sulle operazioni asincrone, ed è qui che ti aiuterà davvero. Se si invia una richiesta sincrona tramite un socket, quando si chiama Socket.Receive(), il thread si bloccherà fino a quando non viene ricevuta una richiesta. Questo non va bene. La tua discussione non può più fare richieste poiché è bloccata. Usando Socket.Beginxxxxxx (), la richiesta I / O verrà effettuata e inserita nella coda IRP per il socket e il thread continuerà. Ciò significa che il tuo thread potrebbe effettivamente effettuare migliaia di richieste in un ciclo senza alcun blocco!

Se ti sto capendo correttamente, stai usando le chiamate via Zeroc-Ice nel tuo codice di test, non in realtà stai cercando di raggiungere un endpoint http. In tal caso, posso ammettere che non so come funziona Zeroc-Ice. Vorrei, tuttavia, suggeriscono seguendo il consiglio elencati qui , in particolare la parte: Consider Asynchronous Method Invocation (AMI). La pagina mostra questo:

Utilizzando AMI, il client riacquista il thread di controllo non appena l'invocazione è stata inviata (o, se non può essere inviata immediatamente, è stata messa in coda), consentendo al client di utilizzare quel thread per eseguire altre attività utili nel frattempo .

Che sembra essere l'equivalente di quello che ho descritto sopra usando i socket .NET. Potrebbero esserci altri modi per migliorare le prestazioni quando provo a fare molti invii, ma vorrei iniziare qui o con qualsiasi altro suggerimento elencato in quella pagina. Sei stato molto vago sulla progettazione della tua applicazione, quindi posso essere più specifico di quanto non sia stato sopra. Ricorda, non utilizzare più thread del necessario per ottenere ciò di cui hai bisogno, altrimenti troverai probabilmente la tua applicazione molto più lenta di quanto desideri.

Alcuni esempi in pseudocodice (ho cercato di renderlo il più vicino possibile al ghiaccio senza che io debba davvero impararlo):

var iterations = 100000;
for (int i = 0; i < iterations; i++)
{
    // The thread blocks here waiting for the response.
    // That slows down your loop and you're just wasting
    // CPU cycles that could instead be sending/receiving more objects
    MyObjectPrx obj = iceComm.stringToProxy("whateverissupposedtogohere");
    obj.DoStuff();
}

Un modo migliore:

public interface MyObjectPrx : Ice.ObjectPrx
{
    Ice.AsyncResult GetObject(int obj, Ice.AsyncCallback cb, object cookie);
    // other functions
}

public static void Finished(Ice.AsyncResult result)
{
    MyObjectPrx obj = (MyObjectPrx)result.GetProxy();
    obj.DoStuff();
}

static void Main(string[] args)
{
    // threaded code...
    var iterations = 100000;
    for (int i = 0; i < iterations; i++)
    {
        int num = //whatever
        MyObjectPrx prx = //whatever
        Ice.AsyncCallback cb = new Ice.AsyncCallback(Finished);
        // This function immediately gets called, and the loop continues
        // it doesn't wait for a response, it just continually sends out socket
        // requests as fast as your CPU can handle them.  The response from the
        // server will be handled in the callback function when the request
        // completes.  Hopefully you can see how this is much faster when 
        // sending sockets.  If your server does not use an Async model 
        // like this, however, it's quite possible that your server won't 
        // be able to handle the requests
        prx.GetObject(num, cb, null);
    }
}

Tieni presente che più thread! = Prestazioni migliori quando provi a inviare socket (o fai davvero qualcosa). I thread non sono magici in quanto risolveranno automaticamente qualsiasi problema su cui stai lavorando. Idealmente, vuoi 1 thread per core, a meno che un thread non stia trascorrendo molto del suo tempo ad aspettare, quindi puoi giustificare di averne di più. Eseguire ogni richiesta nel proprio thread è una cattiva idea, poiché si verificheranno cambi di contesto e spreco di risorse. (Se vuoi vedere tutto ciò che ho scritto al riguardo, fai clic su modifica e guarda le revisioni precedenti di questo post. L'ho rimosso poiché sembrava solo appannare il problema principale a portata di mano.)

Puoi sicuramente fare queste richieste nei thread, se vuoi fare un gran numero di richieste al secondo. Tuttavia, non esagerare con la creazione del thread. Trova un equilibrio e mantienilo. Otterrai prestazioni migliori se usi un modello asincrono rispetto a un modello sincrono.

Spero che aiuti.


Perché parli così tanto delle prestazioni? Non sembra essere quello che vuole l'OP.
svick,

@svick bene il post originale di ops aveva originariamente 4 domande e hanno posto domande sull'esecuzione di attività parallele vs, quindi è stato modificato e ora sono tornate. Quindi, molto di ciò che leggi è stato il risultato. Alla fine, sebbene la sua domanda abbia a che fare con le prestazioni, poiché ha l'idea generale corretta, ma apparentemente manca nella sua implementazione. Credo che le mie risposte puntate alla fine rispondano alla domanda che non ha modificato.
Christopher Currens,

Sono stato costretto a ridurre le mie domande perché volevano votare per chiudere. Ora sembra, è valido qui per averli. @ChristopherCurrens +1 buon punto per la differenza con threadpool alle attività. Ciò ha ampliato la mia comprensione. Ma sono ancora bloccato su come è possibile generare alcune N richieste al secondo? Qual è esattamente il modo migliore per farlo?
Re

@King - Immagino di non essere stato chiaro come pensavo di essere. Gli ultimi 3-4 paragrafi che ho pensato ti avrebbero aiutato. Avevo pensato che stavi già usando una specie di ciclo. Se lo facevi, il problema è che gli invii / ricezioni del tuo socket stanno bloccando e quindi rallentando le tue richieste. Forse troverò un po 'di tempo per pubblicare qualche pseudo codice di esempio.
Christopher Currens,

Non ho alcun problema a inviarli effettivamente su ICE. Il problema è ciò che definisce l'implementazione che creerebbe effettivamente N richieste e qualcosa che si può dire fedele a quel numero, N.
Re

2

Salterò la domanda 1) e andrò subito al n. 2, poiché questo è generalmente un modo accettabile per realizzare ciò che stai cercando. In passato per raggiungere n messaggi al secondo è possibile creare un unico processo che sarà poi lancio p AppDomain. In pratica, ogni AppDomain inizia a eseguire un ciclo di richieste una volta raggiunto un determinato momento (utilizzando un timer). Questa volta dovrebbe essere lo stesso per ogni AppDomain per assicurarsi che inizino a colpire il tuo server nello stesso momento.

Qualcosa del genere dovrebbe funzionare per inviare le tue richieste:

WaitCallback del = state => 
{ 
    ManualResetEvent[] resetEvents = new ManualResetEvent[10000]; 
    WebClient[] clients = new WebClient[10000]; 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index] = new ManualResetEvent(false); 
        clients[index] = new WebClient(); 

        clients[index].OpenReadCompleted += new OpenReadCompletedEventHandler (client_OpenReadCompleted); 

        clients[index].OpenReadAsync(new Uri(@"<REQUESTURL>"), resetEvents[index]); 
    } 

    bool succeeded = ManualResetEvent.WaitAll(resetEvents, 10000); 
    Complete(succeeded); 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index].Dispose(); 
        clients[index].Dispose(); 
    } 
}; 

while(running)
{
    ThreadPool.QueueUserWorkItem(del);
    Thread.Sleep(1000);
}

Questo probabilmente ridurrà le prestazioni su qualsiasi macchina su cui lo stai eseguendo, quindi puoi sempre implementare un tipo simile di loop da più macchine diverse se hai le risorse (usando i processi anziché i domini delle app).

Per la tua terza domanda, dai a questo link una lettura http://www.albahari.com/threading/

Infine, un cronometro dovrebbe essere associato a un contatore di hit per tracciare sia la durata che i colpi unici sul tuo server. Ciò dovrebbe consentire di eseguire alcune analisi dopo il fatto.


2
Quale possibile ragione dovresti creare qui AppDomain separati? Sembra del tutto inutile.
svick

0

Non preoccuparti dei thread, se N è ragionevolmente piccolo. Per generare N richieste al secondo, utilizzare l'ora dell'orologio da parete ( DateTime.Now). Prenditi il ​​tempo sia prima che dopo la richiesta, quindi aggiungi a Sleepper ritardare la richiesta successiva.

Ad esempio, con N = 5 (200 ms):

Before request: 12:33:05.014
After request: 12:33:05.077
Sleep(137)
Before request: 12:33:05.214
After request: 12:33:05.271
Sleep(131)

Questo non è perfetto; potresti scoprire che Sleepnon è esatto. È possibile mantenere un conteggio progressivo delle deviazioni (prima delle X richieste, il tempo dovrebbe essere X-1 / N in seguito) e regolare il periodo di sospensione di conseguenza.

Quando N diventa troppo grande, è sufficiente creare thread M e lasciare che ogni thread generi richieste N / M nello stesso modo.


Devo generare un numero molto elevato di richieste. Quindi questa non può essere l'opzione perché consumerà la mia memoria (4 GB di RAM) anche prima di 100 thread.
Re

Ho creato 20.000 richieste al secondo da un singolo thread, in 250K di codice. Non hai abbastanza CPU per eseguire comunque 100 thread (quella classe di macchine non viene fornita con 4 GB). Il prossimo problema sarebbe quello di respingere tutte quelle richieste; hai 10 Gbit / s Ethernet tra il creatore del carico e il server? Quindi, potresti voler controllare i tuoi reali requisiti.
Salterio del

Per chiarire, ho qualcosa come 20+ Gbps. Quindi non è un problema. A proposito della classe di macchine, a cosa ti riferiresti? numero di processori?
Re

@King: per spingere 100 thread mi aspetterei una macchina a 48 core. SGI vende macchine con un numero così elevato di core, ad esempio, ma su quelle di solito avresti 32 GB o più.
MSalter

0

Il modo più semplice per eseguire test di carico per qualsiasi progetto .NET è acquistare la versione Ultimate di Visual Studio. Questo viene fornito con strumenti di test integrati per aiutare a preformare tutti i tipi di test, inclusi i test di carico. I test di carico possono essere preformati creando utenti virtuali su un singolo PC o distribuiti su più per un numero maggiore di utenti, esiste anche un piccolo programma che può essere installato sui server di destinazione per restituire dati aggiuntivi per la durata del test.

Questo è costoso, ma l'edizione finale ha molte funzionalità, quindi se tutte fossero usate sarebbe un prezzo più ragionevole.


0

Se desideri semplicemente che X thread colpiscano tutti la tua risorsa nello stesso momento, puoi mettere ogni thread dietro un latch di conto alla rovescia e specificare un breve periodo di attesa tra i controlli del semaforo.

C # ha un'implementazione (http://msdn.microsoft.com/en-us/library/system.threading.countdownevent(VS.100).aspx).

Allo stesso tempo, se si esegue lo stress test del proprio sistema, è possibile che si desideri verificare anche le condizioni di gara, nel qual caso si desidera impostare i periodi di sospensione del thread su ciascun thread che oscilla nel tempo con frequenza casuale e picchi / cicli.

Allo stesso modo potresti non voler inviare rapidamente più richieste, potresti avere un successo migliore nel mettere il tuo server in cattivo stato / testare le sue prestazioni del mondo reale impostando un numero minore di thread che impiegano più tempo a consumare e rispedire messaggi e oltre il socket, poiché probabilmente il tuo server dovrà girare i propri thread per gestire i messaggi in corso lenti.

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.