web-dev-qa-db-fra.com

Initialiser un std :: map lorsque la taille est connue à l'avance

Je voudrais initialiser un std::map. Pour l'instant, j'utilise ::insert mais je sens que je perds du temps de calcul car je connais déjà la taille que je veux allouer. Existe-t-il un moyen d'allouer une carte de taille fixe, puis de remplir la carte?

30
vanna

Non, les membres de la carte sont stockés en interne dans une arborescence. Il n'y a aucun moyen de construire l'arborescence tant que vous ne connaissez pas les clés et les valeurs à stocker.

40
Bo Persson

La réponse courte est: oui, c'est possible, mais ce n'est pas anodin. Vous devez définir un allocateur personnalisé pour votre carte. L'idée de base est que votre allocateur personnalisé mettra de côté un seul bloc de mémoire pour la carte. Comme la carte nécessite de nouveaux nœuds, l'allocateur leur attribuera simplement des adresses dans le bloc préalloué. Quelque chose comme ça:

std::map<KeyType, ValueType, std::less<KeyType>, MyAllocator> myMap;

myMap.get_allocator().reserve( nodeSize * numberOfNodes );

Il y a cependant un certain nombre de problèmes à régler.

Tout d'abord, vous ne connaissez pas vraiment la taille de chaque nœud de carte ni le nombre d'allocations que la carte effectuera. Ce sont des détails d'implémentation internes. Vous pouvez expérimenter pour le découvrir, mais vous ne pouvez pas supposer que les résultats se maintiendront sur différents compilateurs (ou même sur les futures versions du même compilateur). Par conséquent, vous ne devez pas vous soucier de l'allocation d'une carte de taille "fixe". Votre objectif devrait plutôt être de réduire le nombre d'allocations nécessaires à une poignée.

Deuxièmement, cette stratégie devient un peu plus complexe si vous souhaitez prendre en charge la suppression.

Troisièmement, n'oubliez pas les problèmes d'alignement de la mémoire. Les pointeurs renvoyés par votre allocateur doivent être correctement alignés pour les différents types d'objets que la mémoire stockera.

Tout cela étant dit, avant d'essayer, assurez-vous que c'est nécessaire. L'allocation de mémoire peut être très coûteuse, mais vous ne devez toujours pas supposer que c'est un problème pour votre programme. Mesurer pour le savoir. Vous devriez également envisager des stratégies alternatives qui permettent plus naturellement une pré-allocation. Par exemple, une liste triée ou un std :: unordered_map.

23
Peter Ruderman

Tu es en train de parler de block allocators. Mais c'est difficile à mettre en œuvre. Mesurez avant de penser à des choses si difficiles. Quoi qu'il en soit Boost a quelques articles sur l'implémentation de l'allocateur de blocs. Ou utilisez une carte préallouée déjà implémentée Stree

2
Denis Ermolin

Je ne sais pas si cela répond à votre question, mais Boost.Container a un flat_map dans lequel vous pouvez réserver de l'espace. Fondamentalement, vous pouvez voir cela comme un vecteur trié de paires (clé, valeur). Mais là encore, si votre entrée était déjà constante, vous n'utiliseriez pas de std::map en premier lieu. Astuce supplémentaire: si vous savez également que votre entrée est triée, vous pouvez utiliser l'insertion avec un indice pour des performances maximales.

1
gast128