IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

set et accélération du programme


Sujet :

C++

  1. #1
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut set et accélération du programme
    Bonjour, je remercie par avance tous ceux qui s'intéresseront à mon problème.

    Je fais un programme de clustering dans lequel je dois à un moment stocker des millions d'objets. Le classe de l'objet est Edge, voici la classe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Edge
    {
    	public:
    		bool operator==(const Edge &other) const;
    		Edge(std::string vertex1, std::string vertex2, int cost);
    		size_t hash() const;
    		std::string vertex1;
    		std::string vertex2;
    		unsigned int cost;
    };
    Voici le coeur du problème :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    for(auto& vertex : vertices){  
    		l++;
    		for(size_t i(0); i < 24; i++){ // 
    			for(size_t j(i); j < 24; j++){
    				std::string v(vertex.first);
    				if(i != j){
    					flip_bit(v[i]); // permet de modifier le ième bit
    					flip_bit(v[j]);
    				}
    				else
    					flip_bit(v[i]);
    				if(vertices.find(v)!= vertices.end()) {
    					Edge e(vertex.first, v, compute_distance(vertex.first, v));
    					std::cout << "coucou " << l << std::endl;
    					graph.insert(e);
    				}
    			}
    		}
    	}
    Mon problème est le suivant : il y a énormément d'objets à rajouter et la structure que j'ai choisie jusqu'ici : unordered_set est trop lente. Un ami à moi fait tourner le programme en moins de 1 min en python, moi ça m'en prends 30.

    Si quelqu'un a une idée, merci de la partager.

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Difficile d'être précis sans voir un peu plus de code... Par exemple, tu parles d'un unordered_set, mais je vois au moins 2 collections : vertices et graph. Laquelle est le unordered_set ? Je n'ai pas trop compris non plus où voulait en venir ton algorithme, donc difficile de voir s'il est efficace ou pas. Tu as essayé de profilé ton programme pour voir si c'est bien dans ce code qu'il passe tout son temps ?



    Sinon, il n'y a pas de raisons qu'un unordered_set soit lent, sauf si tu as une mauvaise fonction de hash.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Salut, je te remercie d'avance et je te copie colle le code en entier.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    #include "clustering_chaud.h"
    #include <iostream>
    #include <array>
    #include <fstream>
    #include <string>
    #include <vector>
    #include <map>
    #include <unordered_set>
    #include <functional>
    #include <cstdlib>
    #include <algorithm>
    #include <set>
     
     
    struct cmp
    {
    	bool operator()(Edge edge, Edge other)
    	{
    		return edge.cost < other.cost and !((edge.vertex1 == other.vertex1 and edge.vertex2 == other.vertex2) or (edge.vertex1 == other.vertex2 and edge.vertex2 == other.vertex1));
     
    	}
    };
     
     
    Edge::Edge(std::string vertex1_, std::string vertex2_, int cost_) : vertex1(vertex1_), vertex2(vertex2_), cost(cost_)
    {
     
    }
     
    typedef std::map<std::string, bool> Vertices;
     
    typedef std::set<Edge, cmp> Graph;
     
    Vertices recuperer_vertices();
    Graph get_graph(Vertices vertices);
    void flip_bit(char& bit);
    int compute_distance(std::string vertex1, std::string vertex2);
     
     
     
    int compute_distance(std::string vertex1, std::string vertex2){
     
    	int distance(0);
     
    	for(size_t i(0); i < vertex1.size(); i++){
    		if(vertex1[i] != vertex2[i])
    			distance++;
    	}
     
    	return distance;
    }
     
     
    void flip_bit(char& bit){
     
    	if(bit == '0')
    		bit = '1';
    	else if(bit == '1')
    		bit = '0';
    	else
    		std::cerr << "Dafuk un bit c'est 0 ou 1" << std::endl;
    }
     
    Vertices recuperer_vertices(){
     
    	Vertices vertices;
    	std::string ligne("");
     
    	std::ifstream fichier("clustering_big.txt");
     
    	if(fichier){
    		getline(fichier, ligne);
     
    		while(getline(fichier, ligne)){
    			std::string vertex("");
     
    			for(size_t i(0); i < 48; i += 2){
    					vertex += ligne[i];
    			}
    			vertices[vertex] = true;
    		}
    	}
     
    	else 
    		std::cerr << "Impossible d'ouvrir le fichier " << std::endl;
     
    	return vertices;
     
    }
     
     
    int main(int argc, char* argv[]){
     
    	Vertices vertices = recuperer_vertices();
     
    	Graph g(get_graph(vertices));
     
    	std::cout << g.max_size() << std::endl;
     
    	Graph s;
     
    	s.insert(Edge("1", "3", 12));
    	s.insert(Edge("4", "1", 10));
    	s.insert(Edge("9", "7", 8));
    	s.insert(Edge("3", "1", 9));
    	s.insert(Edge("10", "3", 9));
    	s.insert(Edge("3", "10", 12));
     
    /*	for(auto& el : g)
    		std::cout << el.vertex1 << " - " << el.vertex2 << " :  " << el.cost << std::endl;
     
    	std::array<int, t> cc;
     
    	for(size_t i(0); i < t; i++)
    		cc[i] = i;
    */
    	return 0;
    }
     
     
    Graph get_graph(Vertices vertices){
     
    	Graph graph;
    	size_t l(0);
     
    	for(auto& vertex : vertices){  
    		for(size_t i(0); i < 24; i++){
    			for(size_t j(i); j < 24; j++){
    				std::string v(vertex.first);
    				if(i != j){
    					flip_bit(v[i]);
    					flip_bit(v[j]);
    				}
    				else
    					flip_bit(v[i]);
    				if(vertices.find(v)!= vertices.end()) {
    					l++;
    					Edge e(vertex.first, v, compute_distance(vertex.first, v));
    					graph.insert(e);
    					if(graph.find(e) != graph.end())
    						std::cout << l << " eme insertion" << std::endl;
    				}
    			}
    		}
    	}
     
    	return graph;
    }
    Comme tu peux le voir j'ai changé pour un set car j'ai besoin d'ordonner mes objets de type Edge. Tu as sans doute raison c'était ma fonction de hachage qui ralentissait le tout car avec ce set "simplifié" (plus besoin de fournir une fonction de hachage) le programme tourne en moins de 1 Min.

    Néanmoins il me reste un problème si tu veux bien m'aider. Lorsque je fais tourner ce programme j'ai vérifié au moyen de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if(graph.find(e) != graph.end())
    						std::cout << l << " eme insertion" << std::endl;
    que mon graphe se remplissait, à la fin du programme je suis censé avoir fait + de 700 000 insertions et pourtant quand je fais std::cout << g.size() << std::endl; j'obtiens 2....

    Je ne comprends absolument pas ce qu'il se passe puisque selon moi chaque fois que je rentre dans if(graph.find(e) != graph.end()) cela veut dire que j'ajoute vraiment une Edge et donc la taille devrait augmenter.

    Pour répondre à ta question, à la fin le programme réalise un clustering, il récupère des sommets dans un fichier et il s'arrange pour que la distance entre chaque cluster soit d'au moins 3 mais le programme là n'est pas terminé il me reste une étape ensuite.

  4. #4
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Ton opérateur de comparaison ne m'a pas l'air correct. Par exemple, on peut avoir a<b qui est faux, et b<a qui est faux aussi, sans que a et b soient équivalents, ce qui n'est pas autorisé. Ca peut expliquer pourquoi il n'y a pas assez d'éléments dans le set (!a<b et !b<a, i les considère comme équivalents, et n'en mettra qu'un des deux dans le set). http://cpp.developpez.com/faq/cpp/?p...l-operateur-lt

    Sinon, en terme de performances, pourquoi passer les Egdes par copie à ton comparateur ? Passe les plutôt par référence constante. Idem pour les arguments de type string (constructeur de Edge). Idem pour l'argument vertices dans get_graph.

    Tes structures de données ont l'air de ne gérer que des bits, mais tu les stockes sous forme de chaînes de caractères, ce qui est dans le meilleur des cas 8 fois trop de place par rapport à ce qui est requis. Je te conseilles plutôt de les stocker dans un uint_least32_t (32 car si j'ai bien compris, tu en a besoin de 24, et 32 est le plus petit type fourni au delà de 24). Ca va complexifier un peu ton code, mais ça devrait être notablement plus performant.

    Tu pourras alors même peut-être te passer d'un set pour gérer les éléments présents : Finalement, un simple tableau de bit à 0 ou 1 pour indiquer la présence de l'élément correspondant tient largement en mémoire quand il s'agit de stocker 2^24 éléments (2Mo suffisent), et du coup, tu remplaces ton accès au set en O(N) par un simple accès indexé (+ décomposition du bit correct), qui devrait être plus rapide. Voire un tableau d'unsigned char, qui prendra 8 fois plus de mémoire, mais évitera de devoir faire des manipulations de bits. Il faudrait bencher voir ce qui est le plus rapide. Donc j'essayerai avec un std::bitset<0x0fff> ou un std::vector<unsigned char>.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  5. #5
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Merci de ta réponse, tu avais raison la comparaison n'était pas correcte, par contre je n'ai pas trouvé d'expression logique qui dise : si les couples (vertex1, vertex2) sont les mêmes (à un swap près) alors les arêtes sont les mêmes sinon tu ranges par ordre croissant des cost. Du coup j'ai dit de trier par cost et j'ai géré les doublons ailleurs.

    Par contre je n'ai pas pu passer vertices par référence constante ça m'a déclenché une erreur à la compilation.

    Merci aussi pour le reste de ta réponse, si j'ai le temps de l'optimiser j'essaierai ce que tu as dit, mais pour le moment c'est trop pour ce que je sais faire alors je vais tâcher de faire fonctionner cette version.

    Par curiosité, si je mets mes sommets qui sont en effet représentés par des chaines de 24 bits dans des uint_least32_t comment est ce que je peux récupérer les 24 premiers bits ?

  6. #6
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Pour savoir si le i-ème bit est vrai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    uint_least32_t vertex;
    bool isSet = vertex & (1 << i);
    Pour le vertices en référence constante : for (auto const & vertex : vertices) au lieu de for (auto & vertex : vertices).

    Pour ta comparaison, je dirais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    	bool operator()(Edge edge, Edge other)
    	{
    		if ((edge.vertex1 == other.vertex1 && edge.vertex2 == other.vertex2) || (edge.vertex1 == other.vertex2 && edge.vertex2 == other.vertex1))
    		{
    			return false; 
    		}
    		return edge.cost < other.cost;
    	}
    Sachant que :
    - 2 vertex de même coût sont alors considérés identiques, ça me semble louche.
    - Une alternative serait de t'assurer que quand tu crées un Edge, tu as toujours vertex1<vertex2, comme ça, tu n'as plus besoin de tester à un swap près.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Par ailleurs, tu pourras alléger ton programme (et surement l'accélérer, du coup) en ne stockant pas les vertex comme des strings dans tes Edge, mais par des entiers. quitte à avoir une map<int, string> pour retrouver les noms.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

Discussions similaires

  1. Accélération programme VBA
    Par boom47 dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 08/06/2012, 14h25
  2. accélération programme trop lent
    Par djocin dans le forum Fortran
    Réponses: 6
    Dernier message: 01/07/2009, 12h30
  3. Réponses: 7
    Dernier message: 04/07/2008, 13h20
  4. Accélération de l'exécution du programme
    Par lammouch dans le forum MATLAB
    Réponses: 8
    Dernier message: 02/04/2008, 13h08
  5. Réponses: 7
    Dernier message: 23/01/2007, 11h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo