Domanda

Sto scrivendo un'applicazione che ha un produttore multiplo, un singolo modello di consumatore (più thread inviano messaggi a un singolo thread di scrittura file).

Ogni thread del produttore contiene due code, una in cui scrivere e una per la quale un consumatore può leggere. Ogni ciclo del thread consumer, scorre attraverso ogni produttore e blocca il mutex di quel produttore, scambia le code, sblocca e scrive dalla coda che il produttore non utilizza più.

Nel ciclo del thread consumer, rimane in attesa per un determinato periodo di tempo dopo aver elaborato tutti i thread del produttore. Una cosa che ho notato immediatamente è che il tempo medio per un produttore di scrivere qualcosa nella coda e tornare è aumentato drammaticamente (di 5x) quando sono passato da 1 thread del produttore a 2. Man mano che vengono aggiunti più thread, questo tempo medio diminuisce fino a quando non raggiunge il fondo fuori - non c'è molta differenza tra il tempo impiegato con 10 produttori contro 15 produttori. Ciò è presumibilmente perché con un numero maggiore di produttori da elaborare, vi è meno contesa per il mutex del thread del produttore.

Sfortunatamente, avendo < 5 produttori sono uno scenario abbastanza comune per l'applicazione e vorrei ottimizzare i tempi di sospensione in modo da ottenere prestazioni ragionevoli indipendentemente da quanti produttori esistono. Ho notato che aumentando il tempo di inattività, posso ottenere prestazioni migliori per conteggi bassi dei produttori, ma prestazioni peggiori per conteggi dei produttori di grandi dimensioni.

Qualcun altro ha riscontrato questo, e in caso affermativo qual è stata la tua soluzione? Ho provato a ridimensionare il tempo di sospensione con il numero di thread, ma sembra in qualche modo specifico per la macchina e piuttosto prova-errore.

È stato utile?

Soluzione

Potresti scegliere il tempo di sonno in base al numero di produttori o persino adattare il tempo di sonno in base a uno schema dyanmic. Se il consumatore si sveglia e non ha lavoro, raddoppia il tempo di sonno, altrimenti dimezzalo. Ma limita il tempo di inattività al minimo e al massimo.

Ad ogni modo, stai documentando un problema più fondamentale. Dormire e sondare è facile da ottenere e talvolta è l'unico approccio disponibile, ma ha molti inconvenienti e non è il & Quot; right & Quot; modo.

Puoi andare nella giusta direzione aggiungendo un semaforo che viene incrementato ogni volta che un produttore aggiunge un articolo a una coda e decrementato quando il consumatore elabora un articolo in una coda. Il consumatore si sveglia solo quando ci sono articoli da elaborare e lo farà immediatamente.

Il polling delle code potrebbe comunque essere un problema. È possibile aggiungere una nuova coda che fa riferimento a qualsiasi coda che contiene elementi. Ma solleva piuttosto la domanda sul perché non si dispone di una singola coda che il consumatore elabora piuttosto che una coda per produttore. A parità di tutti gli altri sembra l'approccio migliore.

Altri suggerimenti

Invece di dormire, consiglierei al tuo consumatore di bloccare una condizione segnalata dai produttori. Su un sistema compatibile con posix, potresti farlo funzionare con pthread_cond. Crea una matrice di pthread_cond_t, una per ciascun produttore, quindi creane una aggiuntiva condivisa tra di loro. I produttori prima segnalano la propria variabile di condizione individuale, quindi quella condivisa. Il consumatore attende la condizione condivisa e quindi scorre gli elementi dell'array, eseguendo un pthread_cond_timed_wait() su ciascun elemento dell'array (utilizzare pthread_get_expiration_np() per ottenere il tempo assoluto per & Quot; ora & Quot; ). Se l'attesa restituisce 0, il produttore ha scritto i dati. Il consumatore deve reinizializzare le variabili di condizione prima di attendere nuovamente.

Usando le attese bloccanti, minimizzerai il tempo che il consumatore blocca inutilmente i produttori. Potresti anche farlo funzionare con i semafori, come affermato in una risposta precedente. I semafori hanno semplificato la semantica rispetto alle condizioni, secondo me, ma dovresti fare attenzione a diminuire il semaforo condiviso una volta per ciascun produttore che è stato elaborato su ogni passaggio attraverso il ciclo del consumatore. Le variabili di condizione hanno il vantaggio di poterle sostanzialmente utilizzare come semafori booleani se le reinizializzate dopo che sono state segnalate.

Prova a trovare un'implementazione di una coda di blocco nella lingua utilizzata per la programmazione. Non sarà sufficiente più di una coda per un numero qualsiasi di produttori e un consumatore.

Per me sembra che tu stia introducendo accidentalmente un po 'di buffering avendo il thread del consumatore impegnato da qualche altra parte, dormendo o facendo un vero lavoro. (la coda funge da buffer) Forse fare un po 'di buffering sul lato produttore ridurrà la tua contesa.

Sembra che il tuo sistema sia molto sensibile alla contesa tra produttore e consumatore, ma sono sconcertato dal motivo per cui un'operazione di swap così semplice occuperebbe abbastanza tempo per essere visualizzato nelle statistiche della tua corsa.

Puoi mostrare del codice?

modifica: forse stai bloccando e scambiando le code anche quando non c'è lavoro da fare?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top