In generale, ho bisogno di una struttura dei dati efficiente in termini di dimensioni simile a std::priority_queue
ma stabile (preservando l'ordine di inserimento).
Aggiungendo solo 4 byte all'oggetto potrei avere 1 byte che serve come priorità e 3 byte come contatore per mantenere l'ordine di inserimento. Il concorrente più vicino - std::forward_list
- aggiungerebbe un overhead di 5 byte (in pratica 8 a causa dell'allineamento) - uno per priorità e 4 per collegamento (architettura a 32 bit). Sarebbe anche più lento di max-heap a causa di attraversamenti quando si aggiunge un nuovo elemento.
Il problema con il contatore è il comportamento del contenitore quando il contatore trabocca. In Boost il contatore ha una lunghezza di 64 bit e quando si rovescia, il contenitore genera un'eccezione ( link ).
Una soluzione a questo problema che viene in mente è il reset del contatore ogni volta che la coda si svuota, ma questa è solo una soluzione parziale al problema.
Esiste un generico (e ottimale - senza attraversare l'intero heap ogni volta) in cui questo potrebbe essere risolto? Ho pianificato di utilizzare std::push_heap()
e std::pop_heap()
da <algorithm>
con una matrice di storage raw, quindi ho accesso a "internals" delle voci. Se possibile, preferirei attenermi a queste funzioni standard invece di implementare il mio heap (o qualsiasi altra struttura di dati) che sarebbe stabile.
EDIT:
Come richiesto in un commento, sto aggiungendo alcune ulteriori informazioni sul mio caso d'uso qui.
Ho bisogno di questa struttura dati per implementare una coda messaggi nel RTOS che sto sviluppando ( link ). Questa coda di messaggi deve seguire tutti i requisiti POSIX e uno di questi è "stabilità" - le nuove voci con la stessa priorità devono essere posizionate DOPO le voci precedenti:
Da link :
A message shall be inserted after other messages in the queue, if any, with equal msg_prio.
Questo è un RTOS per microcontrollori embedded, quindi non posso dare uno scenario di utilizzo specifico, perché ce ne saranno molti. Alcuni dispositivi funzioneranno per un secondo e spegneranno, altri dispositivi potrebbero funzionare per anni senza riavviare. Dato che sto puntando a un design senza limiti, sono principalmente interessato a una soluzione che funzioni in tutti i casi, senza limiti come quella imposta dal contatore semplice, che fallisce quando il contatore trabocca.
Dato che questo è per un microcontrollore, preferirei che la soluzione fosse di dimensioni compatte, quindi soluzioni come "use counter a 128 bit" non sono accettabili.
In generale, vedo due opzioni: utilizzare l'elenco collegato separatamente o utilizzare max-heap (come inizialmente inteso).
L'uso dell'elenco a link singolo aggiungerebbe 5 (in pratica 8 - a causa dell'allineamento) byte a ciascun oggetto archiviato: 1 per priorità, 4 per collegamento. Questa soluzione è stabile "in base alla progettazione", ma l'inserimento di oggetti può essere lento quando ci sono molti oggetti nella lista - perché molti nodi dovranno essere attraversati per trovare lo spot per l'inserimento.
Usando l'heap potrei provare a limitare l'overhead a 4 byte - 1 per priorità, 3 per contatore. Questa opzione potrebbe essere più veloce dell'elenco, ma richiede una soluzione per l'overflow del contatore. Vedo diverse opzioni qui (più possono essere utilizzati allo stesso tempo):
- usa un contatore a 56 bit (l'overhead totale sarebbe di 8 byte per oggetto),
- ripristina il contatore quando la lista diventa vuota (soluzione solo parziale),
- quando si verifica l'overflow, il contatore deve essere resettato, l'intero heap deve essere attraversato e ogni contatore di voci visitate deve essere aggiornato al valore "basso".
Naturalmente so che potrei semplicemente ignorare l'overflow, ma se c'è una soluzione reale, mi piacerebbe implementarla.
Poiché non sembra esserci una soluzione semplice, robusta e deterministica al problema dell'overflow, sto iniziando a orientarmi verso l'uso di - mi aspetto che non ci siano molte voci nella coda dei messaggi (la maggior parte dei microcontrollori ha RAM veramente limitata), quindi il vantaggio di velocità dell'utilizzo dell'heap sarebbe probabilmente trascurabile. Soprattutto quando conto tutte queste copie durante l'inserimento / la cancellazione, mentre il contenuto dell'elenco linkato singolarmente sarebbe per lo più statico in questo senso. E c'è anche l'estrazione "istantanea" dalla testa della lista ...