Quel conteneur STL répondrait le mieux à mes besoins? J'ai essentiellement un conteneur de 10 éléments de large dans lequel je continue Push_back
nouveaux éléments tandis que pop_front
ing l'élément le plus ancien (environ un million de fois).
J'utilise actuellement un std::deque
pour la tâche mais se demandait si un std::list
serait plus efficace car je n'aurais pas besoin de me réallouer (ou peut-être que je me trompe de std::deque
pour un std::vector
?). Ou existe-t-il un conteneur encore plus efficace pour mes besoins?
P.S. Je n'ai pas besoin d'un accès aléatoire
Puisqu'il y a une myriade de réponses, vous pourriez être confus, mais pour résumer:
Utilisez un std::queue
. La raison en est simple: c'est une structure FIFO. Vous voulez FIFO, vous utilisez un std::queue
.
Cela rend votre intention claire à quiconque, et même à vous-même. A std::list
ou std::deque
non. Une liste peut insérer et supprimer n'importe où, ce qui n'est pas ce qu'une structure FIFO est censée faire, et une deque
peut ajouter et supprimer à chaque extrémité, ce qui est également FIFO la structure ne peut pas faire.
C'est pourquoi vous devez utiliser un queue
.
Maintenant, vous avez posé des questions sur les performances. Tout d'abord, rappelez-vous toujours cette règle générale importante: Bon code d'abord, performances en dernier.
La raison en est simple: les personnes qui recherchent la performance avant la propreté et l'élégance finissent presque toujours en dernier. Leur code devient un morceau de bouillie, car ils ont abandonné tout ce qui est bon pour ne rien en tirer.
En écrivant d'abord un bon code lisible, la plupart d'entre vous résoudreont eux-mêmes les problèmes de performances. Et si plus tard vous constatez que vos performances font défaut, il est maintenant facile d'ajouter un profileur à votre code Nice et propre, et de découvrir où est le problème.
Cela dit, std::queue
N'est qu'un adaptateur. Il fournit l'interface sûre, mais utilise un conteneur différent à l'intérieur. Vous pouvez choisir ce conteneur sous-jacent, ce qui permet une grande flexibilité.
Alors, quel conteneur sous-jacent devriez-vous utiliser? Nous savons que std::list
Et std::deque
Fournissent tous les deux les fonctions nécessaires (Push_back()
, pop_front()
et front()
), alors comment décidons-nous?
Tout d'abord, comprenez que l'allocation (et la désallocation) de la mémoire n'est pas une chose rapide à faire, généralement, car cela implique d'aller sur le système d'exploitation et de lui demander de faire quelque chose. Un list
doit allouer de la mémoire à chaque fois qu'un élément est ajouté et le désallouer lorsqu'il disparaît.
Un deque
, d'autre part, alloue en morceaux. Il allouera moins souvent qu'un list
. Considérez-le comme une liste, mais chaque bloc de mémoire peut contenir plusieurs nœuds. (Bien sûr, je vous suggère de vraiment apprendre comment cela fonctionne .)
Donc, avec cela seul, un deque
devrait mieux fonctionner, car il ne traite pas la mémoire aussi souvent. Mélangé au fait que vous manipulez des données de taille constante, il n'aura probablement pas à allouer après le premier passage dans les données, alors qu'une liste sera constamment allouée et désallouée.
Une deuxième chose à comprendre est performances du cache . Sortir à RAM est lent, donc quand le CPU en a vraiment besoin, il tire le meilleur parti de ce temps en reprenant un morceau de mémoire avec lui, dans le cache. Parce qu'un deque
alloue des morceaux de mémoire, il est probable que l'accès à un élément dans ce conteneur entraînera également le CPU à ramener le reste du conteneur. Désormais, tout accès supplémentaire au deque
sera rapide, car le les données sont dans le cache.
Cela ne ressemble pas à une liste, où les données sont allouées une par une. Cela signifie que les données peuvent être réparties partout dans la mémoire et que les performances du cache seront mauvaises.
Donc, considérant cela, un deque
devrait être un meilleur choix. C'est pourquoi il s'agit du conteneur par défaut lors de l'utilisation d'un queue
. Cela dit, ce n'est qu'une supposition (très) éclairée: vous devrez profiler ce code, en utilisant un deque
dans un test et list
dans l'autre pour vraiment savoir avec certitude .
Mais rappelez-vous: faites fonctionner le code avec une interface propre, puis préoccupez-vous des performances.
John soulève la crainte que l'encapsulation d'un list
ou deque
entraîne une baisse des performances. Encore une fois, ni lui ni moi ne pouvons dire avec certitude sans le profiler nous-mêmes, mais les chances sont que le compilateur insère les appels que le queue
fait. Autrement dit, lorsque vous dites queue.Push()
, cela ne fera que dire queue.container.Push_back()
, en ignorant complètement l'appel de fonction.
Encore une fois, ce n'est qu'une supposition éclairée, mais l'utilisation d'un queue
ne dégradera pas les performances, par rapport à l'utilisation du conteneur sous-jacent brut. Comme je l'ai déjà dit, utilisez le queue
, car il est propre, facile à utiliser et sûr, et s'il devient vraiment un profil de problème et un test.
Check-out std::queue
. Il encapsule un type de conteneur sous-jacent et le conteneur par défaut est std::deque
.
Lorsque les performances comptent vraiment, consultez la bibliothèque de tampons circulaires Boost .
Je continue
Push_back
nouveaux éléments tandis quepop_front
ing l'élément le plus ancien (environ un million de fois).
Un million n'est vraiment pas un grand nombre en informatique. Comme d'autres l'ont suggéré, utilisez un std::queue
comme première solution. Dans le cas peu probable où il serait trop lent, identifiez le goulot d'étranglement à l'aide d'un profileur (ne le devinez pas!) Et réinstallez-le à l'aide d'un conteneur différent avec la même interface.
Pourquoi pas std::queue
? Tout ce qu'il a c'est Push_back
et pop_front
.