2013-04-08 8 views
10

Dans mon moteur physique personnalisé, le plus gros goulot d'étranglement est une méthode qui récupère tous les corps du partitionnement spatial (une grille 2D) et renvoie une collection contenant uniquement des pointeurs uniques vers le corps. L'utilisation d'un profileur montre que le goulot d'étranglement est dans la méthode "contains".std :: vecteur plus rapide que std :: unordered_set?

De toute évidence, un std::unordered_set serait la solution "idéale" ici. Cependant, il est beaucoup plus lent que la solution actuelle. J'ai également essayé google::dense_hash_set, qui est plus rapide que std::unordered_set, mais toujours plus lent que la solution actuelle.

const unordered_set<Body*>& GridInfo::getBodiesToCheck() 
{ 
    bodiesToCheck.clear(); 
    for(auto& query : queries) 
     for(auto& body : *query) 
      /*if(!contains(bodiesToCheck, body))*/ bodiesToCheck.insert(body); 
    return bodiesToCheck; 
} 

Pourquoi les conteneurs "corrects" plus lent qu'un std::vector?

Y a-t-il un moyen d'accélérer cette méthode?

+1

Les résultats de profilage sont-ils uniquement pour 'contains'? Rappelez-vous que la recherche peut être plus rapide mais que l'insertion est plus lente que le vecteur. –

+0

Je suppose que vous n'avez pas fait une telle erreur, mais juste pour être vraiment vraiment sûr, vous n'avez pas utilisé 'std :: find' quand vous avez essayé le' std :: unordered_map', n'est-ce pas? –

+0

@stardust_ Le profileur affiche la méthode "getBodiesToCheck()" comme goulot d'étranglement. Si j'utilise la version std :: vector, le goulot d'étranglement à l'intérieur de getBodiesToCheck() (goulot d'étranglement du goulot d'étranglement: P) est l'appel à "contient" –

Répondre

3

Il y a deux possibilités que je peux penser:

  1. Vous avez un nombre assez restreint d'éléments de données qu'une recherche linéaire est plus rapide qu'un hachage-plus comparer recherche.
  2. Vous utilisez la même fonction contains pour rechercher un élément dans le unordered_set, au lieu d'utiliser la fonction membre find.
+3

Comme je me soucie seulement de renvoyer une collection de Body * unique, je n'ai pas utilisé "contains" ou "find" sur unordered_set. J'ai juste utilisé l'insertion en m'attendant à ce qu'il soit seulement rempli d'éléments uniques. –

-2

Voici ce que vous trouverez dans la documentation std:

« conteneurs unordered_set sont plus rapides que les conteneurs fixés pour accéder aux éléments individuels par leur clé, même si elles sont généralement moins efficaces pour l'itération de gamme grâce à un sous-ensemble de leur éléments."

Eh bien, puisque la méthode de recherche finira par boucle à travers un grand nombre d'éléments des thats probablement la raison ...

Peut-être que si vous avez utilisé une fonction de hachage costum vous devriez l'améliorer pour le rendre plus rapide ... La seule chose à laquelle je peux penser ...

+1

Mais encore une fois en utilisant 'unordered_map' il n'y a absolument aucun besoin de' std :: find' de toute façon (et l'OP a confirmé ne pas avoir fait une telle erreur stupide). –

+1

* "Si vous avez vraiment besoin de meilleures performances, le seul conteneur de données auquel je peux penser serait une sorte de table de hachage" * - Euh ... vous voulez dire ... comme un 'std :: unordered_set'? –

+0

Oui, vous avez raison ... Un ensemble non ordonné est en effet une table de hachage ... Mon mauvais. – Mppl

1

Si le nombre de corps dupliqués n'est pas si élevé par rapport aux autres, une option pourrait être de simplement pousser tous vos corps dans le vecteur et supprimer les doublons par la suite. Mais cela nécessitera un std::sort suivi d'un erase(std::unique, end).

Mais cela vaut peut-être la peine d'essayer, étant donné que votre vecteur semble afficher un std::unordered_set de toute façon, qui n'a pas le même emplacement mémoire et un accès trivial comme un std::vector.

+0

Je l'ai essayé, mais la performance est plus lente que ce que j'ai actuellement. –

+0

Je suppose que le downvote restera sans explication? –

0

Je ne suis pas sûr que je comprends le problème correctement, mais il semble que la recherche sera plus lente sur std::vector/std::find, mais l'itération pourrait être plus rapide qu'avec std::unordered_set. Si tel est le cas, et vous n'êtes pas limité par des contraintes de mémoire, vous pouvez mélanger les deux approches:

Maintenir à la fois un std::unordered_set et un std::vector avec les éléments. Recherchez dans le std::unordered_set pour déterminer si l'élément est déjà là, si ce n'est pas le cas, ajoutez-le aux deux conteneurs. À la fin itérer sur le std::vector.

Notez que vous pouvez fournir des indications aux deux conteneurs concernant le nombre «attendu» d'éléments qu'ils contiendront et qui réduira le nombre d'allocations de mémoire/rehashing.

+0

Je suppose que 'std :: unique_set' devrait être' std :: unordered_set'? En dehors de cela, je ne pense pas qu'il soit nécessaire d'itérer sur le 'std :: unordered_set', du moins pas dans l'extrait de code en question (et celui qu'il a profilé et qu'il veut accélérer). C'est juste 'std :: vector + std :: find' vs' std :: unordered_set :: insert', donc dans votre cas il aurait le même overhead comme sa solution 'std :: unordered_set' existante * et * le surcoût de un insert de vecteur. –

+0

@ChristianRau: Oui, «non ordonné» (besoin d'infuser de la caféine immédiatement!) –