Sostituzione per code in RTOS


12

Per la comunicazione tra attività o per condividere dati tra due attività di RTOS, utilizziamo le code. Ma il problema con le code è che sono lenti .... Copiano i dati nel buffer, quindi nella gestione Mutex e quindi nel trasferimento dei dati. È irritantemente lento se devi trasferire dati di grandi dimensioni. Un altro problema è se si accede alla stessa coda da più attività. Quindi l'immagine diventa così: - Prima attendi di accedere a La coda, quindi Gestisci il Mutex interno, quindi Trasferimento dati.

Ciò aumenta le spese generali sul sistema. Quale potrebbe essere la sostituzione efficiente per le code?

(Suppongo che questa domanda sia indipendente da RTOS che utilizziamo. La maggior parte delle RTOS gestisce le code solo in questo modo)


Cosa intendi con una coda a cui accedono più attività? Intendi postare in coda o leggere dalla coda? Più attività dovrebbero essere in grado di pubblicare su una coda con un sovraccarico minimo. RTOS dovrebbe gestire il mutexing in modo che un post sia un'operazione atomica. Per il 99% delle attività, è necessario disporre di un ciclo in sospeso su una coda ed elaborare il messaggio. Una coda dovrebbe (di solito) essere letta da una sola attività. Probabilmente dovrai esaminare il tuo progetto e come stai usando le code invece di sostituirle.
Erik,

@Erik: scusa! Sto usando il meccanismo che hai citato .... Volevo dire qualcos'altro e ho scritto diversi .... Lo modificherò !! Grazie per aver segnalato l'errore! Sto aspettando l'accesso alla coda nel mio codice!
Swanand,

Risposte:


7

Le code funzionano in questo modo perché si tratta di un modello di transazione thread-safe per la comunicazione tra attività. Rischi i problemi di corruzione e / o proprietà dei dati in qualsiasi schema meno rigoroso.

Stai copiando i dati in un buffer in memoria, quindi passando un puntatore con gli elementi della coda o stai provando a passare tutti i dati negli elementi della coda stessi? Se non si passano i puntatori, si otterrà un aumento delle prestazioni facendo questo invece di passare un byte alla volta attraverso gli elementi della coda.


2
Stavo per dire la stessa cosa. Se passi i puntatori ai dati nelle code, puoi aumentare la velocità, ma assicurati di non finire con due thread che tentano di utilizzare e modificare i dati.
Kortuk,

Come ha detto @Kortuk, ho bisogno di "assicurati di non finire con due thread che cercano di utilizzare e modificare i dati" ... Il che significa un aumento del sovraccarico ... Non voglio molta elaborazione! :(
Swanand,

Quindi non esiste un sostituto per le code ... Invece della coda dei dati, devo usare la coda dei puntatori!
Swanand,

1
@Swanand se pianifichi la tua applicazione in modo tale che le code siano solo unidirezionali (ovvero, non leggi mai la stessa coda in due attività) e elabori immediatamente i dati memorizzati sul puntatore, quindi liberali non dovresti avere problemi con la condivisione dei dati. Ci sarà un aumento delle spese generali poiché potrebbe essere necessario creare più code per passare i dati in modo affidabile avanti e indietro, ma questo è il costo di fare affari in un ambiente multi-tasking.
AngryEE,

7

Un modo semplice è mettere un puntatore ai dati sulla coda e consumare i dati usando il puntatore.

Tieni presente che stai scambiando sicurezza per le prestazioni in questo modo in quanto devi assicurarti che:

  1. il buffer rimane valido fino a quando il consumatore non ha consumato i dati
  2. qualcuno dealloca il buffer

Se non si utilizza la memoria allocata in modo dinamico, non è necessario deallocare, ma è comunque necessario assicurarsi che l'area di memoria non venga riutilizzata prima che i dati siano stati consumati.


6

Le code senza blocco possono essere implementate per il caso produttore singolo / consumatore singolo e spesso è possibile progettare il software per ridurre al minimo il numero di code produttore multiplo o consumatore multiplo.

Una coda senza blocchi può essere costruita in questo modo: alloca un array di elementi da comunicare, e anche due numeri interi, chiamali Head e Tail. Head è un indice nell'array, in cui verrà aggiunto l'elemento successivo. La coda è un indice nell'array, in cui è possibile rimuovere l'elemento successivo. L'attività produttore legge H e T per determinare se c'è spazio per aggiungere un elemento; scrive l'elemento nell'indice H, quindi aggiorna H. Le attività del consumatore leggono H e T per determinare se ci sono dati disponibili, legge i dati dall'indice T, quindi aggiorna T. Fondamentalmente si tratta di un buffer ad anello a cui accedono due attività, e il l'ordine delle operazioni (inserire, quindi aggiornare H; rimuovere, quindi aggiornare T) garantisce che non si verifichino danni ai dati.

Se hai una situazione con più produttori e un singolo consumatore, o un singolo produttore e più consumatori, hai effettivamente una limitazione delle risorse di qualche tipo e non c'è nient'altro che utilizzare la sincronizzazione, poiché è più probabile che il limitatore di prestazioni essere il solo produttore / consumatore di un overhead del sistema operativo con il meccanismo di blocco.

Ma se hai più produttori e consumatori, vale la pena spendere il tempo (nello spazio di progettazione) per vedere se non riesci a ottenere un meccanismo di comunicazione più coordinato; in un caso come questo, serializzare tutto attraverso una singola coda rende sicuramente l'efficienza della coda il fattore determinante centrale delle prestazioni.


1
Stavo per fare +1 su questo, ma non sei corretto: le code senza blocco sono possibili da implementare per più lettori e scrittori, sono solo più complicate. (guarda l'articolo di Michael + Scott su Lock-Free Queues google.com/search?q=michael%20scott%20queue )
Jason S,

1
@Jason S - il documento di Scott rivendica specificamente il rientro per le operazioni di inserimento e rimozione senza blocco? In tal caso, se riesci a estrarlo e pubblicarlo, ti preghiamo di farlo, sarebbe un valore inestimabile per molti. Il lettore dovrebbe notare che la carta citata fa uso di speciali istruzioni della macchina, mentre la mia posizione nel post sopra non ha assunto tali istruzioni.
JustJeff,

1
Sì, il costo degli algoritmi senza blocco di solito dipende da CAS o istruzioni equivalenti. Ma come entra in gioco il rientro qui? Ha senso per i mutex + le strutture di blocco, ma non per le operazioni della struttura dei dati.
Jason S,

2

Si può ottenere un funzionamento efficiente in una coda per singolo consumatore multi-produttore priva di blocco se la coda stessa contiene elementi sufficientemente piccoli da funzionare con una primitiva esclusiva di magazzino negozio, confronto-scambio o simile e si può usare un valore riservato o valori riservati per slot di coda vuoti. Quando scrive sulla coda, lo scrittore esegue uno scambio di confronto per provare a memorizzare i suoi dati nello slot vuoto successivo; se fallisce, lo scrittore prova il seguente slot. Sebbene la coda mantenga un puntatore allo slot vuoto successivo, il valore del puntatore è "advisory". Si noti che se un sistema utilizza lo scambio di confronto anziché l'esclusiva di caricamento negozio, potrebbe essere necessario disporre di una "famiglia" di valori "slot vuoti" diversi. Altrimenti, se tra il momento in cui lo scrittore trova uno slot di coda vuoto e tenta di scrivere su di esso, un altro scrittore scrive lo slot e il lettore lo legge, il primo scrittore metterebbe inconsapevolmente i suoi dati in un punto in cui il lettore non li vedrebbe. Questo problema non si verifica nei sistemi che utilizzano l'esclusiva load-store, poiché l'esclusiva store rileverà che i dati sono stati scritti anche se sono stati riscritti nel vecchio valore.


1

È possibile accedere alle code in modo più efficiente scrivendo in cima alla coda Normalmente la maggior parte dei RTOS fornisce il supporto per l'aggiunta alla parte anteriore della coda che non richiede l'acquisizione di mutex. Ma assicurati di usare l'aggiunta al fronte della coda il più minimamente possibile dove vuoi solo eseguire i dati più velocemente. Normalmente le strutture delle code hanno un limite di dimensione massima, quindi è possibile non mettere tutti i dati in coda, quindi passare il puntatore è sempre facile.

Saluti!!


1

Le code non sono intrinsecamente lente. La loro attuazione potrebbe essere.

Se stai copiando ciecamente i dati e stai utilizzando una coda sincrona, vedrai un impatto sulle prestazioni.

Come hanno indicato altri poster, ci sono alternative senza serratura. Il caso del singolo produttore / singolo consumatore è semplice; per più produttori e consumatori, l' algoritmo di coda senza blocchi di Michael e Scott (quelli sono i loro cognomi) è lo standard e viene utilizzato come base per ConcurrentLinkedQueue di Java .

In alcuni casi è possibile ottimizzare la necessità di code, ma offrono garanzie di concorrenza che di solito offrono enormi vantaggi di semplificazione ai sistemi consentendo di disaccoppiare le attività.


Dall'articolo di Michael & Scott: "è il chiaro algoritmo di scelta per le macchine che forniscono una primitiva atomica universale (ad esempio confronto e scambio o carico collegato / negozio condizionale)". Anche se questo potrebbe non bloccare esattamente un thread, qui c'è una forma di sincronizzazione.
JustJeff,

tu hai un punto; può ridurre il requisito di concorrenza dall'accesso esclusivo a una barriera di memoria.
Jason S,
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.