2009-06-29 6 views
1

Je croise un ensemble de 100 000 nombres et un ensemble de 1 000 nombres en utilisant set_intersection en STL et en prenant 21s, où il faut 11ms en C#.Pourquoi set_intersection en STL est-il si lent?

C++ code:

int runIntersectionTestAlgo() 
{ 

    set<int> set1; 
    set<int> set2; 
    set<int> intersection; 


    // Create 100,000 values for set1 
    for (int i = 0; i < 100000; i++) 
    { 
     int value = 1000000000 + i; 
     set1.insert(value); 
    } 

    // Create 1,000 values for set2 
    for (int i = 0; i < 1000; i++) 
    { 
     int random = rand() % 200000 + 1; 
     random *= 10; 

     int value = 1000000000 + random; 
     set2.insert(value); 
    } 

    set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end())); 

    return intersection.size(); 
} 

C# Code:

static int runIntersectionTest() 
    { 
     Random random = new Random(DateTime.Now.Millisecond); 

     Dictionary<int,int> theMap = new Dictionary<int,int>(); 

     List<int> set1 = new List<int>(); 
     List<int> set2 = new List<int>(); 

      // Create 100,000 values for set1 
      for (int i = 0; i < 100000; i++) 
      { 
       int value = 1000000000 + i; 
       set1.Add(value); 
      } 

      // Create 1,000 values for set2 
      for (int i = 0; i < 1000; i++) 
      { 
       int value = 1000000000 + (random.Next() % 200000 + 1); 
       set2.Add(value); 
      } 

      // Now intersect the two sets by populating the map 
     foreach(int value in set1) 
      { 
       theMap[value] = 1; 
      } 

      int intersectionSize = 0; 

     foreach (int value in set2) 
     { 
      int count; 
      if (theMap.TryGetValue(value, out count)) 
      { 
       intersectionSize++; 
       theMap[value] = 2; 
      } 
      } 

      return intersectionSize; 
    } 
} 
+0

Ceci n'est pas un commentaire utile: Est-ce que vous chronométrez le programme entier, ou simplement l'appel de set_intersection()? – Pod

+0

Chronométrez-vous à la fois la création des ensembles initiaux ainsi que l'intersection? –

+2

Vous réalisez que le C++ std :: set est une structure arborescente, alors que le C# Dictionary est une table de hachage basée sur un tableau, et que List n'est qu'un tableau, n'est-ce pas? Avant même de considérer les problèmes d'allocation de votre code, vous comparez des pommes et des oranges. –

Répondre

2

Sur cet ancien Pentium 4 3GHz, j'obtiens 2734 millisecondes pour l'ensemble de la fonction runIntersectionTestAlgo, dans une version de débogage avec des optimisations désactivées. J'ai compilé avec VS2008 SP1.

Si j'accepte les optimisations, j'obtiens 93 millisecondes.

Voici mon code:

#include <set> 
#include <algorithm> 

using namespace std; 

int runIntersectionTestAlgo() 
{ 

    set<int> set1; 
    set<int> set2; 
    set<int> intersection; 


    // Create 100,000 values for set1 
    for (int i = 0; i < 100000; i++) 
    { 
     int value = 1000000000 + i; 
     set1.insert(value); 
    } 

    // Create 1,000 values for set2 
    for (int i = 0; i < 1000; i++) 
    { 
     int random = rand() % 200000 + 1; 
     random *= 10; 

     int value = 1000000000 + random; 
     set2.insert(value); 
    } 

    set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end())); 

    return intersection.size(); 
} 

#include <windows.h> 
#include <iostream> 

int main(){ 
    DWORD start = GetTickCount(); 

    runIntersectionTestAlgo(); 

    DWORD span = GetTickCount() - start; 

    std::cout << span << " milliseconds\n"; 
} 

Désactivation _SECURE_SCL fait aucune différence pour la construction de la libération, qui planait toujours autour des 100 ms.

GetTickCount n'est pas idéal, bien sûr, mais il devrait être assez bon pour distinguer 21 secondes de moins de 100 millisecondes. Donc, je conclus qu'il y a quelque chose qui ne va pas avec vos tests de performance.

+0

Wow. J'ai pris votre code, l'ai exécuté, et il a couru en 31ms. J'ai ensuite couru avec le débogueur attaché (comme je l'avais fait pour mes tests) et il a couru en 23353 ms. –

+0

STL utilise beaucoup de fonctions intermédiaires qui doivent être intégrées pour que la performance soit acceptable. GCC au moins vous permettra d'activer le débogage et l'optimisation simultanément, et les versions récentes de GDB peuvent afficher et parcourir les appels de fonctions inline, vous pouvez donc déboguer ces choses à une vitesse supportable;) Vous pouvez essayer d'activer sans autres optimisations de la vitesse que vous obtenez. –

8

Un couple de choses feraient vos deux exemples plus comparables. Tout d'abord, votre exemple en STL n'est pas tout à fait correct, d'une part, les deux ensembles doivent être triés par ordre croissant (en STL, on parle d'un "strict weak ordering"). Deuxièmement, vous utilisez des «ensembles» qui sont implémentés comme des arbres dans la liste STL, par opposition aux «listes» qui sont des listes liées. Les insertions aléatoires dans un ensemble sont plus coûteuses que l'insertion à la fin d'une liste. Essayez d'utiliser une liste d'ints dans l'exemple C++ et de trier la liste en premier (sinon l'insertion ne fonctionnera pas correctement) et je pense que vous verrez des résultats plus favorables.

5

Je couru votre code C++ sur mon linux

$ time ./test 

real 0m0.073s 
user 0m0.060s 
sys  0m0.003s 

21s signifie pour moi vous compilez sans optimisations. Dans le cas où vous utilisez MSVC assurez-vous que vous avez répertorié _SECURE_SCL=0 (voir msdn) dans les définitions de compilation. Sinon, toutes les opérations de l'itérateur STL sont lentes.

+0

+1 pour _SCL_SECURE. Je n'avais pas entendu parler de ce drapeau avant. Savez-vous s'il est désactivé pour les versions de versions? –

+1

Ce sujet a fait l'objet de discussions à la liste de diffusion boost. Ils ont dit que _SCL_SECURE est activé (défini sur 1) par défaut, même en mode Release. –

+0

Il est activé par défaut dans les versions de débogage et de publication. Selon une présentation au BoostCon de cette année, il sera désactivé dans les versions de versions de VS2010. :) – jalf

1

J'ai mis à jour votre exemple pour utiliser un code de minuteur que j'utilise lors des tests unitaires. Sur ma machine, je reçois les horaires suivants (sur la base): -O3

First loop 0.0040654 
Second loop 4.8e-05 
Intersection 0.000349 
Intersection size: 50 

Sur cette base, si je lis mes décimales correctement, il faut « 4ms » insérer les éléments dans le premier set, 50 microsecondes pour insérer les éléments dans le deuxième ensemble et un tiers de ms pour effectuer l'intersection.

Je ne parviens pas à exécuter votre exemple C# sur ma machine, donc je ne peux pas comparer le timing, mais ce n'est certainement pas 21s lorsque vous postez.

0

Vos codes C# et C++ fonctionnent différemment. Le code C# utilise des astuces de hachage magiques pour la vitesse, votre code C++ utilise des astuces pour la vitesse.Une chose qui va accélérer les choses sans doute (en ignorant le fait que votre test semble être rompu) serait d'utiliser le hachage, comme suit:

  1. Créer une hash_map de l'une des deux collections.
  2. Effectuez une itération sur chaque élément de la deuxième collection. Si hash_map1 contient cet élément, ajoutez-le à votre résultat.
+0

Oui, ils sont différents. J'ai précédemment essayé l'approche C# en C++ en utilisant chacun de std: map, stdext: hash_map et boost :: unordered_set et obtenu les mêmes mauvais résultats. Je suis sûr que si j'ai changé le code C# pour utiliser HashSet et .IntersectWith ce serait aussi rapide (ou peut-être plus rapide). –