8
votes

Intersection rapide des ensembles: C ++ vs c #

sur ma machine (quad core, RAM de 8 Go), exécutant Vista X64 Business, avec Visual Studio 2008 SP1, j'essaie d'intersecter deux séries de chiffres très rapidement.

J'ai mis en œuvre deux approches en C ++, et un en C #. L'approche C # est plus rapide jusqu'à présent, j'aimerais améliorer l'approche C ++ afin de pouvoir être plus rapide que c #, que je m'attends à ce que c ++ puisse faire. P>

Voici la sortie C #: (Builée de sortie) P >

// MapPerformance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>

#include <boost\unordered\unordered_map.hpp>

#include "timer.h"

using namespace std;
using namespace stdext;
using namespace boost;

int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_map<int,int> theMap;  

    vector<int>::const_iterator set1_end = set1.end();

    // Now intersect the two sets by populating the map
    for ( vector<int>::const_iterator iterator = set1.begin(); iterator != set1_end; ++iterator )
    {
        int value = *iterator;

        theMap[value] = 1;
    }

    int intersectionSize = 0;

    vector<int>::const_iterator set2_end = set2.end();

    for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
    {
        int value = *iterator;

        unordered_map<int,int>::iterator foundValue = theMap.find(value);

        if ( foundValue != theMap.end() )
        {
            theMap[value] = 2;

            intersectionSize++;
        }
    }

    return intersectionSize;

}

int runSetIntersection(const vector<int> set1_unsorted, const vector<int> set2_unsorted)
{   
    // Create two vectors
    std::vector<int> set1(set1_unsorted.size());
    std::vector<int> set2(set2_unsorted.size());

    // Copy the unsorted data into them
    std::copy(set1_unsorted.begin(), set1_unsorted.end(), set1.begin());
    std::copy(set2_unsorted.begin(), set2_unsorted.end(), set2.begin());

    // Sort the data
    sort(set1.begin(),set1.end());
    sort(set2.begin(),set2.end());

    vector<int> intersection;
    intersection.reserve(1000);

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

    return intersection.size(); 
}

void createSets( vector<int>& set1, vector<int>& set2 )
{
    srand ( time(NULL) );

    set1.reserve(100000);
    set2.reserve(1000);

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

    // Try to get half of our values intersecting
    float ratio = 200000.0f / RAND_MAX;


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

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

    // Make sure set1 is in random order (not sorted)
    random_shuffle(set1.begin(),set1.end());
}

int _tmain(int argc, _TCHAR* argv[])
{
    int intersectionSize = 0;

    vector<int> set1, set2; 
    createSets( set1, set2 );

    Timer timer;
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest(set1, set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runSetIntersection(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    getchar();

    return 0;
}
  • Les ensembles C ++ sont maintenant correctement configurés afin d'avoir une intersection de 50% (comme le C #) li>
  • Set1 est mélangé de sorte que ce n'est pas trié, Set2 n'était déjà pas trié li>
  • L'implémentation SET_InterSection utilise maintenant des vecteurs et les trie d'abord li> ul>

    c ++ (version, x64) Résultats: p> xxx pré>

    donc son 2x plus lent que c #. @Jalf: Vous obtenez des nombres assez rapides, y a-t-il quelque chose que je fais mal ici? P>

    C ++ Code: P>

    Found the intersection (using unordered_map) 1000 times, in 21580.7ms
    Found the intersection (using set_intersection) 1000 times, in 22366.6ms
    


12 commentaires

Je doute que c # pourrait faire une tâche telle que cela nettement plus rapide. Vous devriez regarder des performances très similaires.


Votre timing dans les deux cas est défectueux. Si vous êtes intéressé par le temps nécessaire pour effectuer la compensation, vous devriez faire le temps, pas le temps nécessaire pour construire et remplir les listes / vecteurs.


Je ferais du temps le test C # avec System.Diagnostics.stopwatch, il est beaucoup plus précis que la méthode DateTime qui peut être désactivée par plusieurs millisecondes.


Pour commencer, je regarderais le profilage du code pour déterminer quelle (s) partie (s) prenons la majeure partie du temps. Je ne suis pas un programmeur Win32 mais j'imagine qu'il doit y avoir quelque chose de similaire à Oprofile / GProf sur Linux.


@Julianr: D'accord. Sûr les mêmes lignes; J'envisagerais d'augmenter la taille des tests ou d'exécuter le test 1000 fois - il est beaucoup plus facile d'avoir confiance en timings lorsque vous pouvez réellement voir qu'ils prennent> 1s


JulianR: Je l'ai mis à jour pour utiliser System.Diagnostics.stopwatch


Dave Rigby: Je l'ai mis à jour pour exécuter le test 1000 fois.


JALF: ​​Je l'ai mis à jour pour ne pas inclure la population des listes / vecteurs.


La classe de la minuterie, quelle heure de minuterie utilise-t-elle?


La minuterie utilise QueryPerformanceFrequency et QueryPerformEcanceCounter.


@ Dave Rigby - La classe STOPWATCH est effectivement beaucoup plus facile à mettre en œuvre que dans votre exemple. C'est fondamentalement chronomètre sw = stopwatch.startnew (); sw.stop (); Console.writeine ​​(sw.elapsed);


Essayez d'exécuter le code que j'ai posté. Je suis curieux de quelles timings vous y arrivez.


13 Réponses :


4
votes

Utilisez ceci ...

vector<int> set1(10000);
vector<int> set2(1000);


7 commentaires

Ou réserve d'appel (1000), mais continuez à utiliser push_back


Ou tout simplement pas l'inclure dans votre timing en premier lieu. Votre code de configuration ne doit pas faire partie de la référence.


Je souhaite inclure le temps de configuration, car mes données ne sont pas dans l'une de ces structures de données au début ... Donc, si l'approche me demande de le mettre dans une structure de données, je dois inclure ce TME.


@jalf. Point équitable. J'avais supposé qu'il voulait effectivement mesurer cela mais aussi ...


Je conviens qu'il existe une vaste différence entre initialiser quelque chose à 10000 octets pour le C # et faire plusieurs récidives sur le côté C ++.


Néanmoins, cela ne fait pas partie de ce test. Vous obtiendrez des données d'analyse comparative beaucoup plus utiles en les traitant comme des problèmes distincts. Premièrement, souciez de la performance d'intersection, puis effectuez un test séparé pour la partie "Construire des structures de données initiales".


J'ai modifié la mise en œuvre C ++ qui utilise commandé_set et vecteur de réserve d'appel () sur les vecteurs. Pas beaucoup de changement.



0
votes

Je sais que votre solution fonctionne bien, mais avez-vous essayé d'utiliser les implémentations STL:


1 commentaires

Mon code C ++ inclut une implémentation à l'aide de Set_InterSection. Je vais jeter un oeil à "Comprend".



27
votes

Il y a plusieurs problèmes avec votre test.

D'abord, vous ne testez pas ensemble intersection, mais « créer deux tableaux, les remplir avec des nombres aléatoires, puis effectuez l'intersection ensemble ». Vous ne devriez chronométrer la partie du code que vous êtes réellement intéressé. Même si vous allez vouloir faire ces choses, ils ne devraient pas être comparés ici. Mesurer une chose à la fois, de réduire l'incertitude. Si vous voulez que votre implémentation C ++ pour exécuter mieux, vous devez d'abord savoir quelle partie de celui-ci est plus lente que prévu. Ce qui signifie que vous devez code d'installation séparé de test d'intersection. P>

En second lieu, vous devez exécuter le test d'un grand nombre de fois pour prendre des effets de mise en cache possibles et d'autres incertitudes en compte. (Et probablement une sortie de temps total pour, disons, 1000 courses, plutôt que d'un temps individuel pour chacun. De cette façon, vous réduisez l'incertitude de la minuterie qui pourrait avoir une résolution limitée et faire rapport des résultats inexacts lorsqu'ils sont utilisés dans les 0-20ms gamme.

de plus, pour autant que je peux lire des documents, l'entrée set_intersection doivent être triés, qui set2 ne sera pas. Un il semble y avoir aucune raison d'utiliser unordered_map code> , lorsque unordered_set code> serait un match beaucoup mieux pour ce que vous faites. p>

a propos du code de configuration étant nécessaire, notez que vous avez probablement ne pas em > besoin de vecteurs de remplir afin d'exécuter l'intersection. les deux votre propre implémentation et set_intersection code> travail sur itérateurs déjà, de sorte que vous pouvez les passer simplement une paire de itérateurs aux structures de données vos entrées sont déjà. p>

quelques commentaires plus précis sur votre code: p>

  • Utilisez ++ iterator code> au lieu de iterator ++ code> li>
  • plutôt que d'appeler vector.end () à chaque itération de la boucle, l'appeler une fois et mettre en cache le résultat li>
  • expérimenter en utilisant des vecteurs triés vs std :: set vs unordered_set code> (pas unordered_map code>) li> Ul>

    Modifier strong> p>

    Je ne l'ai pas essayé votre version C #, donc je ne peux pas comparer les chiffres correctement, mais voici mon test modifié. Chacun d'eux est exécuté 1000 fois, sur un Core 2 Quad 2,5 GHz avec 4 Go de RAM: p>

    #define _SECURE_SCL 0
    
    #include <ctime>
    #include <vector>
    #include <set>
    #include <iostream>
    #include <algorithm>
    #include <unordered_set>
    #include <windows.h>
    
    template <typename T, typename OutIter>
    void stl_intersect(const T& set1, const T& set2, OutIter out){
        std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), out);
    }
    
    template <typename T, typename OutIter>
    void sort_stl_intersect(T& set1, T& set2, OutIter out){
        std::sort(set1.begin(), set1.end());
        std::sort(set2.begin(), set2.end());
        std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), out);
    }
    
    
    template <typename T>
    void init_sorted_vec(T first, T last){
        for ( T cur = first; cur != last; ++cur)
        {
            int i = cur - first;
            int value = 1000000000 + i;
            *cur = value;
        }
    }
    
    template <typename T>
    void init_unsorted_vec(T first, T last){
        for ( T cur = first; cur != last; ++cur)
        {
            int i = rand() % 200000 + 1;
            i *= 10;
    
            int value = 1000000000 + i;
            *cur = value;
        }
    }
    
    struct resize_and_shuffle {
        resize_and_shuffle(int size) : size(size) {}
    
        void operator()(std::vector<int>& vec){
            vec.resize(size);
    
        }
        int size;
    };
    
    int main()
    {
        srand ( time(NULL) );
        std::vector<int> out(100000);
    
        std::vector<int> sortedvec1(100000);
        std::vector<int> sortedvec2(1000);
    
        init_sorted_vec(sortedvec1.begin(), sortedvec1.end());
        init_unsorted_vec(sortedvec2.begin(), sortedvec2.end());
        std::sort(sortedvec2.begin(), sortedvec2.end());
    
        std::vector<int> unsortedvec1(sortedvec1.begin(), sortedvec1.end());
        std::vector<int> unsortedvec2(sortedvec2.begin(), sortedvec2.end());
    
        std::random_shuffle(unsortedvec1.begin(), unsortedvec1.end());
        std::random_shuffle(unsortedvec2.begin(), unsortedvec2.end());
    
        std::vector<int> vecs1[1000];
        std::vector<int> vecs2[1000];
    
        std::fill(vecs1, vecs1 + 1000, unsortedvec1);
        std::fill(vecs2, vecs2 + 1000, unsortedvec2);
    
        std::set<int> set1(sortedvec1.begin(), sortedvec1.end());
        std::set<int> set2(sortedvec2.begin(), sortedvec2.end());
    
        std::tr1::unordered_set<int> uset1(sortedvec1.begin(), sortedvec1.end());
        std::tr1::unordered_set<int> uset2(sortedvec2.begin(), sortedvec2.end());
    
        DWORD start, stop;
        DWORD delta[4];
    
        start = GetTickCount();
        for (int i = 0; i < 1000; ++i){
            stl_intersect(set1, set2, out.begin());
        }
        stop = GetTickCount();
        delta[0] = stop - start;
    
        start = GetTickCount();
        for (int i = 0; i < 1000; ++i){
            stl_intersect(uset1, uset2, out.begin());
        }
        stop = GetTickCount();
        delta[1] = stop - start;
    
        start = GetTickCount();
        for (int i = 0; i < 1000; ++i){
            stl_intersect(sortedvec1, sortedvec2, out.begin());
        }
        stop = GetTickCount();
        delta[2] = stop - start;
    
        start = GetTickCount();
        for (int i = 0; i < 1000; ++i){
            sort_stl_intersect(vecs1[i], vecs1[i], out.begin());
        }
        stop = GetTickCount();
        delta[3] = stop - start;
    
        std::cout << "std::set_intersection on std::set: " << delta[0] << "ms\n";
        std::cout << "std::set_intersection on tr1::unordered_set: " << delta[1] << "ms\n";
        std::cout << "std::set_intersection on sorted vectors: " << delta[2] << "ms\n";
        std::cout << "std::set_intersection on unsorted vectors: " << delta[3] << "ms\n";
    
    
        return 0;
    }
    


25 commentaires

D'accord, le test n'est pas strictement défini sur l'intersection, il inclut également de remplir toutes les structures de données nécessaires au test. J'ai mis à jour le test pour exécuter 1000 fois.


Remplir les structures de données ne fait pas partie de Ce test . Mesure que séparément. Vous introduisez une énorme quantité d'incertitude qui invalide essentiellement vos résultats.


J'ai modifié le code et réaffecter les points de repère avec la population des structures de données effectuées avant les tests chronométrés.


Jalf: Cela me semble que STD :: Set est une structure de données triée. sgi.com/tech/stl/set.html


Oh, c'est vrai, je pensais que tu courais aussi sur les vecteurs. (Pourquoi n'es-tu pas?)


Parce que le premier exemple que j'ai trouvé de Comment utiliser Set_InterSection l'a montré fonctionnant sur des ensembles. Si vous pensez que le passage à des vecteurs accélérera les choses, je vais essayer d'essayer.


Je ne sais pas si cela va accélérer, mais Set_InterSection fonctionne sur tout itérateur d'entrée, tant qu'il est une séquence triée (donc si vous utilisez des vecteurs, vous devrez le trier d'abord)


@Jalf: J'ai modifié le code pour passer des vecteurs triés en Set_InterSection. Si je les ai triés avant le test, l'intersection a été effectuée dans 352 ms (1000 fois!) Si vite. Mais si je fais le tri dans le test, il faut 2401ms.


2401 est toujours deux fois aussi bon que votre code C #, n'est-ce pas?


Oui, 2401ms deux fois plus vite que c # et très bon .. je l'aime. Je me demande simplement si c'est un test valide que les données de Set1 sont déjà triées (mes données réelles ne sont pas). Je vais regarder à mettre à jour le test pour travailler sur deux tests aléatoires (non triés).


Hey Jalf, c'est génial, très rapide .. cependant, serait-il facile de régler afin que les vecteurs soient résolus à chaque fois? Mes données ne sont pas triées, alors je vais devoir le faire. Dans la vie réelle, cela ne sera pas exécuté 1000 fois sur des vecteurs pré-triés.


Terminé. Je teste avec des vecteurs pré-triés et non formés maintenant. On dirait que les deux sets et un usered_set sont beaucoup plus rapides que dans vos propres tests cependant. Pouvez-vous reproduire les mêmes résultats si vous exécutez mon code?


Joli. Une autre question: Set1 est déjà triée ... J'essaie de trouver un bon moyen de le randiratiser tout en veillant à ce qu'il ait toujours des valeurs de 0 à 100 000 (à l'exclusion du décalage).


On dirait que STD :: Random_shuffle peut faire ça :)


J'ai posté une référence et code mis à jour ci-dessous, je ne vois pas les résultats rapides fous, vous êtes Jalf. (C'était rapide, jusqu'à ce que je plaisante soit Set1 et trié à chaque fois)


Vous obtenez maintenant les mêmes heures que je reçois, environ 10 à utiliser Set_InterSection sur des listes non formées. Ceci est une amélioration 2x sur l'endroit où nous avons commencé, mais toujours 2x plus lent que c #.


Est-ce que cela importe cependant? Si défini ou non astendu_set est toujours 4 fois plus rapide que c #


SET: Le tri est effectué en dehors de la référence ... alors ne semble donc pas juste. Unommked_set: cela fonctionne-t-il? Vous passez ce qui ressemble à un ensemble non formé à set_intersection ...


Ensemble est une structure de données commandée, rappelez-vous? Donc, si l'entrée qu'il est construite est triée ou non pas d'importance. Il trie de toute façon son entrée sur l'insertion. À propos de Unommanded_set, je pense que vous avez raison, ce n'est pas valide, car cela n'est évidemment pas trié. N'avait pas vraiment pensé à cela. :)


Ensemble: Oui, si essentiellement, il "trie" lorsque vous insérez .. Les insertions sont plus lentes dans des ensembles que dans les vecteurs. Donc, si j'ai commencé avec mes données non traitées, cela me faudra plus longtemps pour l'obtenir dans un ensemble d'un vecteur.


J'ai ajusté le code C # maintenant, pour shuffer set1, il n'a pas fait de différence. La méthode C # ne se soucie pas si les ensembles sont triés ou non.


Oui, j'ai réalisé la même chose. J'ai encore mis en place le même algorithme en C ++, et jusqu'à présent, il y a environ 600 ms dessus, mais je pense que j'ai un bug ou deux, il s'agira donc de quelques minutes avant de le publier.


Cool - Une chose à faire est de générer le nombre d'articles dans l'intersection, en tant que vérification de la santé mentale (il devrait être ~ 500)


Oui, c'était un bug, testant l'intersecte (set2, set2) :) Eh bien, c'est du coucher pour moi. Je vais ajouter un peu à ma réponse, puis descendez. :)


Ok, posté ma mise à jour finale pour ce soir. Pas de nouveau code ni de points de repère, mais quelques astuces sur ce que vous devriez probablement essayer ensuite. Je pourrais lui donner un coup de feu demain. :)



0
votes

sont des drapeaux d'optimisation C ++ allumé?


4 commentaires

lesquels? L'optimisation est définie sur "maximiser la vitesse (/ O2)"


Je définit "la taille de la faveur ou la vitesse" pour "favoriser le code rapide (/ ot)", aucune différence.


Avez-vous trouvé ce qui prenait la plupart du temps?


Non, je n'ai pas compris ce qui prend la plupart du temps. Dans le test Set_InterSection, il n'y a pas beaucoup de choses sur l'appelant Set_InterSection :)



0
votes

OK, après de nombreux commentaires, j'ai mis à jour la question initiale plusieurs fois:

  • Les tests sont maintenant à chaque exécution de 1 000 fois
  • Le code C # utilise maintenant une minuterie de résolution supérieure
  • Les structures de données sont maintenant remplacées avant les tests

    Le résultat de cela jusqu'à présent est que c # est toujours ~ 5 fois plus rapide que c ++.

    Merci à tous pour vos idées / suggestions.


0 commentaires

9
votes

Un problème que je vois tout de suite, c'est que vous passez les ensembles en C ++ par la valeur et non par la référence Const. Donc, vous les copiez chaque fois que vous les transmettez!

En outre, je n'utiliserais pas un ensemble pour la cible de Set_InterSection . J'utiliserais quelque chose comme xxx

ce code, cependant, alloue toujours à l'intérieur de la fonction. Encore plus rapide serait xxx

puis allouez-y à la scrupule avant de démarrer la minuterie.

cependant, si vous cherchez la taille, une main -und for Loop, combiné à Set :: Recherche pourrait donner des résultats encore meilleurs.


1 commentaires

Bonne tache! Cela devrait compter pour une partie de la lenteur.



0
votes

mise à jour:

J'ai modifié le code SET_InterSection pour utiliser des vecteurs et pour les trier (au lieu d'utiliser la classe de jeu de tri), et c'est beaucoup plus rapide maintenant: p>

// MapPerformance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>

#include <boost\unordered\unordered_map.hpp>

#include "timer.h"

using namespace std;
using namespace stdext;
using namespace boost;

int runIntersectionTest(vector<int> set1, vector<int> set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_map<int,int> theMap;

    // Now intersect the two sets by populating the map
    for ( vector<int>::iterator iterator = set1.begin(); iterator != set1.end(); iterator++ )
    {
        int value = *iterator;

        theMap[value] = 1;
    }

    int intersectionSize = 0;

    for ( vector<int>::iterator iterator = set2.begin(); iterator != set2.end(); iterator++ )
    {
        int value = *iterator;

        unordered_map<int,int>::iterator foundValue = theMap.find(value);

        if ( foundValue != theMap.end() )
        {
            theMap[value] = 2;

            intersectionSize++;
        }
    }

    return intersectionSize;

}

int runSetIntersection(vector<int> set1, vector<int> set2)
{   
    sort(set1.begin(),set1.end());
    sort(set2.begin(),set2.end());

    set<int> intersection;

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

    return intersection.size(); 
}



int _tmain(int argc, _TCHAR* argv[])
{
    srand ( time(NULL) );

    vector<int> set1;
    vector<int> set2;

    set1.reserve(10000);
    set2.reserve(1000);

    // Create 100,000 values for set1
    for ( int i = 0; i < 100000; i++ )
    {
        int value = 1000000000 + i;
        set1.push_back(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.push_back(value);
    }

    int intersectionSize = 0;


    Timer timer;
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest(set1, set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runSetIntersection(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    getchar();

    return 0;
}


3 commentaires

Ne réussissez pas les vecteurs par valeur !! Const Vector & Set1 est la manière C ++.


Bon appel, erreur bâclée de ma part essayant de changer rapidement le code.


Vous pouvez écrire les résultats d'intersection à un vecteur au lieu d'un ensemble (devrait être beaucoup plus rapide). Aussi je m'attendais à ce que Unorded_set soit beaucoup plus rapide que des ensembles simples



2
votes

Je modifierais le C ++ "RunnterSectiontestest" pour prendre des références constantes aux conteneurs plutôt que de les disposer de copier à chaque appel. (Le code C # utilisera des réfs.)


0 commentaires

1
votes

Étant donné que vous utilisez Visual Studio, vous devez vérifier si vous avez _secure_scl défini sur 1 (typiquement si vous n'avez pas explicitement défini, ce sera 1). S'il est défini, tout le code STL sera coché de la plage, même dans les constructions de libération. Ralentissement généralement le code down de 10-15%.

Il semble que Microsoft n'était pas conscient que, par exemple, std :: vecteur a déjà une interface si vous voulez la vérification de la plage: std :: vecteur :: at ()!

(Désolé, devait l'enlever de ma poitrine).

Quoi qu'il en soit, l'inefficacité principale est que vous copiez les conteneurs au lieu de les transmettre par la valeur. Utilisez des références à (essayer de) comparer des pommes et des pommes au lieu de pommes et de bananes.


1 commentaires

Je vais ajuster et republier sans copier ... J'ai défini _secure_scl à 0. (#define _secure_scl 0)



2
votes

Il peut aussi valoir la peine d'envisager le boost Set disjoint conteneur, qui est spécialement optimisé pour certains types d'opérations définies grandes.

Il fonctionne en traitant un groupe d'ensembles en tant que syndicats de plusieurs ensembles disjoints, permettant de construire d'autres ensembles, tels que des intersections ou des syndicats très à moindre coût, une fois que l'ensemble initial des ensembles disjoints est construit. Si vous prévoyez de faire beaucoup d'opérations définies sur des ensembles qui ne changent pas beaucoup, vous pouvez probablement vous attendre à ce que cela soit très rapide. Si, d'autre part, vous utiliserez chaque ensemble une fois et je vais le jeter, cela ne va probablement pas faire trop.

Quoi qu'il en soit, vous vous feriez de votre faveur pour expérimenter au moins cela pour voir si cela vous donne une bosse dans votre cas spécifique.


1 commentaires

Cette structure m'a juste donné un frisson, car il contient la première utilisation pratique de la fonction Ackermann, ou plutôt son inverse. Incroyable!



0
votes

Vous passez toujours la valeur des vecteurs. Ce qui serait bien si vous ne les copiez pas aussi.

Inserteur ne mettait pas les valeurs à la fin du vecteur où est-il rapide. Cela n'a fait que cela sur le premier insert après cela, il a inséré la valeur au début de la matrice (où l'extrémité utilisée pour pointer). P>

Vous où recherchez la valeur deux fois dans la version de la carte de hachage, lorsque vous mis à jour la valeur. Pourquoi cette mission de valeur est-elle mise à jour? P>

Exécutez ce code et publiez vos horaires. P>

// MapPerformance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>

#include <boost\unordered\unordered_set.hpp>

#include "timer.h"

using namespace std;
using namespace stdext;
using namespace boost;

int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_set<int> theSet;      

     theSet.insert( set1.begin(), set2.end() );

    int intersectionSize = 0;

    vector<int>::const_iterator set2_end = set2.end();

    for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
    {
        if ( theSet.find(*iterator) != theSet.end() )
        {
                intersectionSize++;
        }
    }

    return intersectionSize;
}

int runSetIntersection( vector<int> set1, vector<int> set2)
{   
    // Sort the data
    sort(set1.begin(),set1.end());
    sort(set2.begin(),set2.end());

    vector<int> intersection;
    intersection.reserve(1000);

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

    return intersection.size(); 
}

void createSets( vector<int>& set1, vector<int>& set2 )
{
    srand ( time(NULL) );

    set1.reserve(100000);
    set2.reserve(1000);

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

    // Try to get half of our values intersecting
    float ratio = 200000.0f / RAND_MAX;


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

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

    // Make sure set1 is in random order (not sorted)
    random_shuffle(set1.begin(),set1.end());
}

int _tmain(int argc, _TCHAR* argv[])
{
    int intersectionSize = 0;

    vector<int> set1, set2;     
    createSets( set1, set2 );

    Timer timer;
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest(set1, set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runSetIntersection(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    getchar();

    return 0;
}


1 commentaires

J'ai fait ces changements, aucune différence notable. Votre nouvelle runnInterSectiontest est similaire dans la performance à l'ONUORDEDED_MAP ONE (environ 2 fois plus lente que SET_InterSection)



0
votes

Dernier référence:

Code:

// MapPerformance.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <unordered_set>

#include <boost\unordered\unordered_map.hpp>

#include "timer.h"

using namespace std;
using namespace stdext;
using namespace boost;
using namespace tr1;


int runIntersectionTest2(const vector<int>& set1, const vector<int>& set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_set<int> theSet;      

     theSet.insert( set1.begin(), set1.end() );

    int intersectionSize = 0;

    vector<int>::const_iterator set2_end = set2.end();

    for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
    {
        if ( theSet.find(*iterator) != theSet.end() )
        {
                intersectionSize++;
        }
    }

    return intersectionSize;
}

int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
    // hash_map<int,int> theMap;
    // map<int,int> theMap;
    unordered_map<int,int> theMap;  

    vector<int>::const_iterator set1_end = set1.end();

    // Now intersect the two sets by populating the map
    for ( vector<int>::const_iterator iterator = set1.begin(); iterator != set1_end; ++iterator )
    {
        int value = *iterator;

        theMap[value] = 1;
    }

    int intersectionSize = 0;

    vector<int>::const_iterator set2_end = set2.end();

    for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
    {
        int value = *iterator;

        unordered_map<int,int>::iterator foundValue = theMap.find(value);

        if ( foundValue != theMap.end() )
        {
            theMap[value] = 2;

            intersectionSize++;
        }
    }

    return intersectionSize;

}

int runSetIntersection(const vector<int>& set1_unsorted, const vector<int>& set2_unsorted)
{   
    // Create two vectors
    std::vector<int> set1(set1_unsorted.size());
    std::vector<int> set2(set2_unsorted.size());

    // Copy the unsorted data into them
    std::copy(set1_unsorted.begin(), set1_unsorted.end(), set1.begin());
    std::copy(set2_unsorted.begin(), set2_unsorted.end(), set2.begin());

    // Sort the data
    sort(set1.begin(),set1.end());
    sort(set2.begin(),set2.end());

    vector<int> intersection;
    intersection.reserve(1000);

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

    return intersection.size(); 
}

void createSets( vector<int>& set1, vector<int>& set2 )
{
    srand ( time(NULL) );

    set1.reserve(100000);
    set2.reserve(1000);

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

    // Try to get half of our values intersecting
    float ratio = 200000.0f / RAND_MAX;


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

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

    // Make sure set1 is in random order (not sorted)
    random_shuffle(set1.begin(),set1.end());
}

int _tmain(int argc, _TCHAR* argv[])
{
    int intersectionSize = 0;

    vector<int> set1, set2; 
    createSets( set1, set2 );

    Timer timer;
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest(set1, set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runSetIntersection(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    timer.Reset();
    for ( int i = 0; i < 1000; i++ )
    {
        intersectionSize = runIntersectionTest2(set1,set2);
    }
    timer.Stop();

    cout << "Found the intersection of " << intersectionSize << " values (using unordered_set) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;

    getchar();

    return 0;
}


2 commentaires

Le code que j'ai posté délibérément n'a pas dépassé les vecteurs par référence, de sorte que le compilateur les copierait implicitement. Si vous souhaitez le faire votre chemin, remplacez au code avant le tri avec ceci: Vector set1 (non astendu_set1); Vecteur set2 (désaltement_set2); De cette façon, les vecteurs sont copiés efficacement. Le code tel qu'il est debout remplit chaque vecteur avec des zéros. Ensuite, il copie la valeur souhaitée sur le dessus. La méthode ci-dessus alloue suffisamment d'espace, puis copie les valeurs du paramètre sans missions supplémentaires. Cela accélérera les choses, bien que je ne connaisse pas à quel point


Je ne suis pas sûr que je vous sois à 100%. J'ai fait un changement cependant, j'ai fait une introduction à la diffusion, prenez le vecteur pas de vecteur & (donc cela reçoit une copie), puis supprimé le code à l'intérieur de cette fonction qui copie le vecteur. Cela n'a pas fait de différence notable. Je suis d'accord en général, le repère serait plus juste si le vecteur n'était pas copié à chaque fois.



2
votes

Au fait, si vous avez de grands ensembles triés STD :: Set_InterSection n'est pas l'algorithme le plus rapide. STD :: Set_InterSection prend jusqu'à 2 * (m + n) -1 comparaisons, mais des algorithmes comme celui de Baeza-Yates peuvent être plus rapides. Pour les petits m, Baeza-Yates est O (m * journal (n)), tandis que pour n = alpha * m il est O (n). L'idée de base est de faire une sorte de recherche binaire à 2 voies.

http://citeeerx.ist.psu.edu/viewdoc/download?di=10.1.91.7899&rep=rep1&type=pdf

Analyse expérimentale d'un rapide Algorithme d'intersection pour les séquences triées Ricardo Baeza-Yates et Alejandro Salinger

ou

r. Baeza-Yates. Un algorithme d'intersection réglé rapide pour les séquences triées. Dans Procédure du 15e symposium annuel sur la correspondance des motifs combinatoires (CPM 2004), Springer LNCS 3109, PP 400-408, Istanbul, Turquie, juillet 2004.

ci-dessous est une explication et une mise en œuvre d'Erik Frey où il présente des résultats significativement plus rapides que STD :: Set_InterSection avec une sonde binaire. Je n'ai pas encore essayé son code. de
http://fawx.com/

  1. Choisissez l'élément médian, A, dans le ensemble plus petit.
  2. recherche de son élément d'insertion-position, B, dans le plus grand ensemble.
  3. Si A et B sont égaux, appendez l'élément à la résultat.
  4. Répétez les étapes 1 à 4 sur des sous-ensembles non vides de chaque côté des éléments A et B.

    ;

    xxx


0 commentaires