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 :

Performance et algorithmie en C++


Sujet :

C++

  1. #21
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 960
    Points
    32 960
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par ABD-Z Voir le message
    En fait le sort je le fais pour chaque mot que j'envoie en tant que clef.
    Imaginons nous sommes dans l'index XXX de la liste et on a le mot "marre". Je le trie, donc "marre" devient "aemrr", et ainsi je fais map["aemrr"].push_back(XXX).
    Mais bon je crois que cette méthode est nul, même avec smoothsort... Je vais comme ce que tu m'as compter les lettres que je stocke dans une map que je stocke en tant que clef dans une autre map. Donc, lieu d'avoir unordered_map<string, vector<int>>, j'aurais unordered_map<map<char,int>, vector<int>>
    Je vois pas bien par quelle noeud au cerveau tu finis avec une map comme clé ? Ou un vector<int> en valeurs ?
    La clé c'est la structure de représentation. Et les valeurs sont bien entendus les mots...
    Je l'ai pourtant écrit noir sur blanc
    À terme, tu as une collection de map<représentation, mots anagrammes ayant cette représentation>.
    Qui sera bien sûr de la forme std::map<representation, std::vector<std::string>>

    Tu as un premier jet qui utilise un short pour chaque lettre : ok, mais on peut beaucoup mieux faire.
    Déjà, une lettre sera jamais présente un nombre négatif de fois. Donc unsigned.
    Ensuite, une lettre peut être présente... 10 fois au max dans un mot ? Mettons 10 pour simplifier.
    La représentation ne sera rien de plus qu'un nombre à 26 chiffres. Les unités ? Ça s'appelle A. Les dizaines B, ... exactement de la même façon que tu comptes depuis toujours il me semble, ou juste comment fonctionne l'écriture d'un nombre en base 10. Et la base peut être adaptée.
    Mais commençons avec cette base 10.
    Un u64 max ça vaut 18,446,744,073,709,551,615. C'est 20 chiffres, donc on va en utiliser 19 pour être sûr (donc [a,s]).
    Il en reste 8, que l'on peut rentrer dans un u32.
    Et bien pour créer la représentation, il s'agit d'un bête parcours de la chaîne et d'opérations triviales.
    À vue de nez :
    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
    struct Repr {
      uint64_t a{0};
      uint32_t b{0};
    };
    Repr Compute(const std::string& str)
    {
      Repr ret;
      for (char c : str)
      {
        if (c < 't')
        {
          uint64_t index = pow(10, c - 'a');
          ret.a += index;
        }
        else
        {
          uint32_t index = pow(10, c - 't');
          ret.b += index;
        }
      }
      return ret;
    }
    Et personnellement je serais partisan de recréer un pow utilisant des entiers qui pourrait être plus performant et dont les résultats devrait être plus précis que celui du standard qui utilise des flottants.
    Ou une fonction qui fait un gros switch pour chaque caractère et retourne directement le multiplier.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  2. #22
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut
    Effectivement, les structures en tant que clefs ne sont pas si triviales que ça.
    Ma structure contient 28 champs comprenant les lettres de l'alphabet plus deux signes (tirer et apostrophe).
    J'ai donc rajouté la fonction operator== dans la structure qui, comme son nom l'indique, teste si les champs de deux structures sont égals. J'ai testé deux méthodes : -une avec une boucle parcourant les champs et qui retourne FAUX lorsque une égalité n'est pas respectée et donc VRAI lorsque toutes les égalités le sont. -la deuxième étant de faire un retourne bourrin avec les 27 &&. Celles avec la boucle for m'a semblé un petit chouia meilleure, mais ce n'est pas perceptible que ça.
    J'ai dû aussi une classe/structure de hashage contenant la fonction operator(). J'ai optimisé la fonction de hashage en ne calculant uniquement les champs qui une valeur supérieure à 0.
    Donc, pour chaque champs qui a une valeur supérieur à 0, j'effectue un hash combine comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <class T>
    inline void hash_combine(std::size_t & s, const T & v)
    {
    	std::hash<T> h;
    	s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
    }
    Bien sûr, dans la fonction qui parcourt la liste de mots, je ne fais pas de copie de chaînes de caractères, j'initialise à chaque fois ma structure, et fonction de la lettre rencontrée, j'incrémente le champ correspondant.
    Et bien devinez quoi!
    Ma technique de copie de string puis smoothsort n'a pas à rougir face aux structures! Malgré toutes mes optimisations, mettre une structure de 28 champs dans une unordered_map s'est avéré plus lent...
    Il ne me reste plus qu'à tester avec une map en tant que clef, il y a moyen que cela soit plus performant.

    Citation Envoyé par Bousk Voir le message
    ...
    C'est super intéressant comme optimisation. Merci pour la piste. Et en plus c'est assez facile pour tester l'égalité.
    J'avais bizarrement pas vu ton message quand j'ai posté le mien sur la structure en tant que clef.
    Je vous en donnerai des nouvelles

  3. #23
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut Performance correcte
    Bonjour,
    Je viens de faire à peu près ce que Bousk m'a dit de faire. Sauf que dans ma représentation, une structure, j'utilise deux champs de 64 bits. J'ai regardé les fréquences des lettres en français, pour optimiser le temps de calcul, et j'ai donc représenté deux champs de cette manière :
    champ0 'wjyfvgmdltnie
    champ1 -zhxqhbpcuorsa

    Comme on peut le voir, plus on est vers la droite plus les lettres sont fréquentes. J'ai donc codé une fonction pow, mais à la fin je me suis rendu compte que ça ne servait à rien puisque je peux en fonction de la lettre, attribuer une valeur (unité, dizaine, centaine etc).

    Cette méthode est nettement bien plus performante que celle avec smoothsort. Je gagne environ 400 ms de différence.
    Cependant, j'ai une dernière petite question concernant la méthode de hashage. En effet, notre structure contenant les deux champs de 64 bits qui comptent les lettres doivent être hashés.
    J'utilise la méthode de hashage de boost :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <class T>
    inline void hash_combine(std::size_t & s, const T & v)
    {
    	std::hash<T> h;
    	s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
    }
    Et donc dans la fonction operator(), j'appelle deux fois la fonction hash_combine car nous avons deux champs dans notre structure. Il n'y a pas moyen de réduire le nombre d'appel de hash_combine à 1?
    J'ai essayé en faisant hash_combine(hash_result, structure.champ0 + structure.champ1), ça marchait, mais je sais que c'est pas top car on peut risquer des collisions.

  4. #24
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 960
    Points
    32 960
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par ABD-Z Voir le message
    Je gagne environ 400 ms de différence.
    Quasi 25%, pas mal.

    Si tu crains les collisions de hash, utilise une map et l'opérateur < plutôt qu'une unordered map ?
    Tu peux aussi certainement avoir un packing plus malin et faire tenir le tout sur 64 bits.
    L'exemple avec base 10 était là juste pour introduire le concept et te permettre de le comprendre pour te l'approprier et l'adapter.
    Avec 3 bits par lettre, il faut 84 bits pour les 26 lettres et 2 symboles. Il faut donc sauver 20 bits.
    3 bits ça permet d'avoir 7 occurrences d'une lettre ou symbole.
    Je pense que la plupart des mots n'auront jamais 7 fois une lettre donnée ?
    Je pense aussi que certaines lettres, et les symboles, ne se retrouvent jamais plus de 4 fois dans un mot, ce qui permet de les représenter sur 2 bits, voire 1 seule fois (l'apostrophe ?), et ils tiennent alors sur 1 bit.
    Et si tout rentre sur 64 bits, plus aucun problème de hashing ou autre.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #25
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Si tu crains les collisions de hash, utilise une map et l'opérateur < plutôt qu'une unordered map ?
    C'est une piste aussi à explorer. Mais map est apparemment plus lent. Corrigez-moi si j'ai tort.

    Citation Envoyé par Bousk Voir le message
    Quasi 25%, pas mal.
    Avec 3 bits par lettre, il faut 84 bits pour les 26 lettres et 2 symboles. Il faut donc sauver 20 bits.
    3 bits ça permet d'avoir 7 occurrences d'une lettre ou symbole.
    Je pense que la plupart des mots n'auront jamais 7 fois une lettre donnée ?
    Bien-sûr la plupart des mots n'auront pas 7 fois une lettre donnée quoique si pour le e et les autres lettres qui se répètent facilement. On peut vérifier grâce à ce site : https://www.forum.exionnaire.com/dic...re-dans-un-mot
    Mais, j'essaye en même temps de faire quelque chose de relativement absolu, c'est-à-dire de faire un programme qui peut fonctionner avec n'importe quel mot même inventé.

  6. #26
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 324
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 324
    Points : 4 134
    Points
    4 134
    Par défaut Répétition ?
    Bonjour,

    Je ne suis pas sûr que le facteur de répétition soit un plus. Il diminue potentiellement la taille mais pas suffisamment pour la complexité engendrée.

    Les 26 lettres (plus le - ?) se codent sur 5 bits ce qui permet de stocker directement des mots jusqu'à 25 caractères dans un entier de 128 bits. Pas assez pour les 27 lettres de intergouvernementalisations mais les mots de longueur record sont uniques et n'ont donc pas d'anagramme.

    A mon sens, il faudrait encoder l'anagramme pivot (mot bidon avec en poids fort les lettres de plus faibles valeurs, 0 restant l'absence de lettre) en même temps que l'encodage du mot dans un entier. Je crains que cette partie de bas niveau soit très typée C.

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  7. #27
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Quasi 25%, pas mal.
    Si tu crains les collisions de hash, utilise une map et l'opérateur < plutôt qu'une unordered map ?
    Avec la map normal, bizarrement cest bien plus performant. J'attends les 1360 ms! Soit un gain de 300 ms!
    Je trouve ça effectivement étrange parce que les unordered_map sont censées être bien plus rapides.
    Voici la différence entre les deux types de map :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
                     | map             | unordered_map
    ---------------------------------------------------------
    Ordering        | increasing  order   | no ordering
                    | (by default)        |
     
    Implementation  | Self balancing BST  | Hash Table
                    | like Red-Black Tree |  
     
    search time     | log(n)              | O(1) -> Average 
                    |                     | O(n) -> Worst Case
     
    Insertion time  | log(n) + Rebalance  | Same as search
     
    Deletion time   | log(n) + Rebalance  | Same as search
    A chaque insertion, on fait du log(n) a chaque fois avec la map triée ce qui est beaucoup plus lent que que l'insertion d'une unordered_map théoriquement.
    Après, peut-être que mon calcul de hashage peut être améliorée... J'appelle deux fois cette fonction (car ma structure dispose de deux champs) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <class T>
    inline void hash_combine(std::size_t & s, const T & v)
    {
    	std::hash<T> h;
    	s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
    }
    J'utilise la fonction de std hash, je sais pas si elle peut être améliorée aussi.
    Est-ce que vous avez des idées sur ce point?

  8. #28
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2017
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Israël

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2017
    Messages : 34
    Points : 14
    Points
    14
    Par défaut
    Bonjour,
    La discussion est de haut niveau technique, et je remarque que l'on cherche à grater quelques millisecondes...
    Ne serait-il pas plus productif de regarder du côté de l'algorithme?
    Il ne ressort pas clairement si ce que tu recherches est un anagramme complet de l'entrée ou bien aussi partiel.
    Dans les deux cas tu gagnerais énormément en créant un second dictionnaire où les lettres de chaque mot sont classées en ordre alphabétique. (vélo devient elov) avec des pointeurs vers le vrai dico.
    Une seule entrée par mot (vélo et vole sous la même entrée : elov, donc deux pointeurs l'un vers vélo l'autre vers vole)
    Si c'est un anagramme complet qu'il te faut plus aucune comparaison à faire entre les lettres des mots ! Tu classes juste les lettres de l'entée en ordre alphabétique et tu la cherche dans le nouveau dictionnaire.
    S'il te faut des parties de mots tu y gagnes aussi. En utilisant un algorithme de tri approprié la recherche sera bien plus rapide.
    Et en fait on peut ici aussi créer un algorithme qui évite pas mal de comparaisons, mais bon c'est déjà un poil plus complexe, mais je ne sais même pas si tu en a utilité, et puis ça peut se deviner.
    Bonne journée.

  9. #29
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Legro Voir le message
    Si c'est un anagramme complet qu'il te faut plus aucune comparaison à faire entre les lettres des mots ! Tu classes juste les lettres de l'entée en ordre alphabétique et tu la cherche dans le nouveau dictionnaire.
    Shalom!
    Alors c'est exactement ce que j'ai fait initialement (je sais pas si tu as lu les messages de la première page car ici c'est la page 2). Et on m'a fait remarqué que cette technique, même si c'est un bon début quand même à vrai dire, elle est plus ou moins naze car je fais une copie de chaîne de caractères que je trie.
    Avec std sort c'est toujours du n*log(n), tandis qu'avec smoothsort (meilleur algo de tri pour avoir les meilleures performances) on a une complexité de 1 quand c'est déjà trié.
    Pour te résumer, au lieu de stocker des string triés en tant que clefs dans ma hashmap (que tu nommes dictionnaire parce que je devine que tu fais du Python), j'ai créer un structure de deux champs de 64 bits qui comptent lettres et c'est cette structure qui est la clef de la map (ou hashmap).

  10. #30
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2017
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Israël

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2017
    Messages : 34
    Points : 14
    Points
    14
    Par défaut
    Bon, très bien alors dans ce cas pas la peine de passer sur tout le dictionnaire,
    tu as les mots débutant par la lettre 'a' qui sont les plus nombreux, et ceux débutants par xyz quasi inexistants
    (puisqu'un seul mot de la langue française ne contient que des 'z' : zzzz ; et aucun que des 'z' et des 'y').
    Donc tu divise le dictionnaire en sous catégories, de façon à avoir disons une centaine de sous-groupes ;
    par exemple le groupes des 'aa' qui doit être assez conséquent. Puis de 'ab' à 'ad',
    (enfin je ne dispose pas des tailles exactes de chaque groupe, mais bon dans l'idée).
    Et tu ne recherche ton mot que dans le groupe qui lui correspond.
    A moins que ça aussi tu ne l'ais déjà fait ?

  11. #31
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2017
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Israël

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Octobre 2017
    Messages : 34
    Points : 14
    Points
    14
    Par défaut
    En fait je me rends compte que je n'avais rien compris au problème.
    L'idéal serait que le mot lui-même soit l'indice dans ton dictionnaire.
    Dans ce cas je te propose une idée un peu farfelue, mais qui fera peut-être l'affaire dans la mesure où l'on sait d'avance quelle est la liste des mots.
    Il s'agit de créer un système numérique de base 29 (26 caractères de l'alphabet, l'apostrophe, le tiret, et un signe nul – qu'il n'est pas nécessaire de représenter, mais qui doit exister.)
    Ensuite créer un premier tableau où tu n'introduis que les mots contenant jusqu’à six ou sept lettres (en fonction des capacités de ton système 29^6 ça fait déjà beaucoup) chaque mot (ou pointeur vers le mot) dans la case ayant sa propre valeur en base 29. (il restera alors pas mal de cases vides, mais en s'en fiche)
    Ensuite tu fais un deuxième tableau de 2 dimensions cette fois, où le premier indice sont les 6 ou 7 premières lettres et le second les lettres suivantes.
    S'il le faut tu crée un troisième tableau à trois dimensions pour les mots encore plus longs.
    A l'entrée tu vérifie la taille du mot et tu l'envois vers le tableau correspondant à sa taille, directement vers sa case.
    Là je pense qu'on est au top de l'optimisation - si toutefois j'ai compris ce que tu recherches.

  12. #32
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 960
    Points
    32 960
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par ABD-Z Voir le message
    Bien-sûr la plupart des mots n'auront pas 7 fois une lettre donnée quoique si pour le e et les autres lettres qui se répètent facilement. On peut vérifier grâce à ce site : https://www.forum.exionnaire.com/dic...re-dans-un-mot
    Mais, j'essaye en même temps de faire quelque chose de relativement absolu, c'est-à-dire de faire un programme qui peut fonctionner avec n'importe quel mot même inventé.
    Tu dois adapter la solution au problème. Et le problème doit être défini et fixé.
    Il est utopique de vouloir une solution optimisée pour un truc générique.
    Tu peux aisément compresser ça sur un u64. Tu peux aussi utiliser un u128.
    Mais tu peux pas avoir une solution optimisée parce que maintenant tu veux accommoder un gus qui va inventer un mot qui s'écrit avec 8w, 12z et 3'. Ou parce que tu veux appliquer des mots anglais, puis russe et chinois.
    J'ai expliqué comment faire, et des pistes d'amélioration. Une implémentation doit prendre ~1h.
    Qu'as-tu fait depuis ? Pourquoi on ne voit pas de nouveau code ? Pourquoi ton seul souci est le calcul du hash quand tu peux t'en passer avec l'ajout d'un opérateur < ou avec une meilleure compression ?
    Compresser ça sur un unique u64 est ta meilleure chance.
    Sauf à trouver une hashmap qui prenne des hash 128bits. Auquel cas, utilise un unique champ u128 et sers-t-en de hash.
    Créer un hash n'est pas une opération instantanée.
    Comment fais-tu pour parser une chaîne ? Pour extraire chaque index à computer ? Utilises-tu un switch ? Une jump table ? Des opérations binaires ou complexes ? Des instructions spécifiques ?
    As-tu vérifié le code généré pour voir si des optimisations autres peuvent être faîtes ?
    Que mesures-tu dans tes performances ? Doit-on deviner les options de compilation utilisée ?
    As-tu regardé du côté de Boost si une collection ou algorithme meilleure que la STL existe ?
    Parallélises-tu le tout ? Utilises-tu un unique thread/coeur ? Pourquoi ou pourquoi pas ?
    Je vais pas aller à la pêche aux informations si tu ne veux rien donner.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  13. #33
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Legro Voir le message
    Dans ce cas je te propose une idée un peu farfelue, mais qui fera peut-être l'affaire dans la mesure où l'on sait d'avance quelle est la liste des mots.
    Effectivement, trop farfelue pour être réalisable.
    Je ne sais pas si tu as effectivement lu les précédents posts, mon problème est le suivant :
    -en entrée j'ai une liste de mots (plus de 10000), que ça soit en français, en anglais, en flamand, triés dans l'ordre alphabétique.
    Je dois donc parcourir cette liste. La meilleur technique c'est de parcourir le mot en une seule fois et de compter le nombre de lettres que l'on sauvegarde dans une structure. Cette dernière est notre clef.
    Je pense que cette méthode est la plus rapide, il n'y a rien à faire, surtout avec des map, le programmes s'exécute en 1365 ms.

    Ma question maintenant se tourne sur un problème un petit peu particulier, comment optimiser la fonction de hashage pour les unordered map.
    Je voulais avoir des avis sur cette fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <class T>
    inline void hash_combine(std::size_t & s, const T & v)
    {
    	std::hash<T> h;
    	s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
    }
    Citation Envoyé par Bousk Voir le message
    Qu'as-tu fait depuis ? Pourquoi on ne voit pas de nouveau code ? Pourquoi ton seul souci est le calcul du hash quand tu peux t'en passer avec l'ajout d'un opérateur < ou avec une meilleure compression ?
    Mais si si si! J'ai implémenté l'opérateur < pour les map triés et le résultat est bien plus rapide! Je l'ai écris! (10/08/2020)
    Et j'ai été surpris que ça soit bien plus rapide car les map sont plus lentes que les unordered_map, c'est pour cette raison que je demande en ce moment des conseils sur la fonction de hashage car je soupçonne l'efficacité de ma fonction de hashage.

    Je vais répondre aux quesetion
    Citation Envoyé par Bousk Voir le message
    Comment fais-tu pour parser une chaîne ? Pour extraire chaque index à computer ? Utilises-tu un switch ? Une jump table ? Des opérations binaires ou complexes ? Des instructions spécifiques ?
    As-tu vérifié le code généré pour voir si des optimisations autres peuvent être faîtes ?
    Que mesures-tu dans tes performances ? Doit-on deviner les options de compilation utilisée ?
    As-tu regardé du côté de Boost si une collection ou algorithme meilleure que la STL existe ?
    Parallélises-tu le tout ? Utilises-tu un unique thread/coeur ?
    Je parcours la chaîne de caractère lettre par lettre. ... J'utilise un switche pour compter en fonction de la lettre. Jump table non. Non. Non.
    Concernant les optimisations, je suis sur visual studio, je sais pas si c'est optimisable.
    La fonction hash_combine est celle de boost que j'ai copié collé dans mon code.
    Pas de parallélisme.

  14. #34
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 324
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 324
    Points : 4 134
    Points
    4 134
    Par défaut Simple et brutal
    Bonjour,

    Proposition algo simple et brutal :

    • Liste d'enregistrements int128 mcd mot codé, int128 acd anagramme standard codé (les mots qui ne tiennent pas dans 128 bits (chaque caractères majuscule sous 5 bits genre ch - ' ') n'ont pas d'anagramme.
    • Duplication de la liste : L1 triée par mcd (tri peut être inutile si la liste provient d'un dictionnaire déjà trié), L2 triée par acd (a priori pas d'indirection qui ferait gagner 44% de place mais ralentirait les recherches )
    • Recherche d'anagramme : recherche dichotomique du mot dans L1 puis recherche dichotomique de l'anagramme pivot dans L2 soit 14 accès + un séquentiel amont-aval de recherche des autres anagrammes (moyenne < 6 accès).


    70000 mots de la langue française => 2 listes de 2,240 Mo qui tiennent en mémoire. Un tri rapide devrait mettre de l'ordre de 340 ms (en comptant 1 us par comparaison permutation) sans compter que le tri rapide peut être multithread.

    Un travail d'accélération de la recherche et de la diminution de la taille serait un post traitement (one shot) qui supprimerait tous les mots qui n'ont pas d'anagramme (c'est en O(n log n) également).

    Ce n'est qu'une simple piste.

    Salutation
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  15. #35
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 960
    Points
    32 960
    Billets dans le blog
    4
    Par défaut
    Et as-tu essayé de mettre la représentation dans un u128 et utiliser cette valeur comme clé de map ? À défaut de profiter de hashmap, comparer des u128 devrait être relativement rapide pour le processeur pour parcourir l'arbre interne.
    Pour le hash je ne sais pas trop. Tu pourrais peut-être juste utiliser le premier champ, ou un xor des 2 champs. Le risque c'est les collisions, mais c'est souvent exagéré et s'applique pas si souvent au final.
    Sinon, tu améliores la compression et fais tenir le tout sur un unique u64, et utilise cette valeur comme hash. Là tu auras le meilleur cas possible, puisque tu pourras utiliser une hashmap, n'auras pas à créer de hash et ne risque aucune collision.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  16. #36
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Guesset Voir le message
    Bonjour,

    Proposition algo simple et brutal :

    • Liste d'enregistrements int128 mcd mot codé, int128 acd anagramme standard codé (les mots qui ne tiennent pas dans 128 bits (chaque caractères majuscule sous 5 bits genre ch - ' ') n'ont pas d'anagramme.
    • Duplication de la liste : L1 triée par mcd (tri peut être inutile si la liste provient d'un dictionnaire déjà trié), L2 triée par acd (a priori pas d'indirection qui ferait gagner 44% de place mais ralentirait les recherches )
    • Recherche d'anagramme : recherche dichotomique du mot dans L1 puis recherche dichotomique de l'anagramme pivot dans L2 soit 14 accès + un séquentiel amont-aval de recherche des autres anagrammes (moyenne < 6 accès).
    Bonjour, j'ai lu plusieurs fois ta proposition d'algo simple et brutal, mais je n'ai pas compris grand chose
    mcd c'est la liste, vector, qui contient tous les mots du dico trié dans l'ordre alphabétique, on est d'accord.
    Ensuite, acd, c'est la liste qui contient les anagrammes... Dans un premier temps, on duplique mcd dans acd si j'ai bien compris.
    Après la recherche dichotomique, j'ai pas trop bien compris comment tu fais la dichotomie. Y a un truc qui m'échappe.

    Citation Envoyé par Bousk Voir le message
    Et as-tu essayé de mettre la représentation dans un u128 et utiliser cette valeur comme clé de map ? À défaut de profiter de hashmap, comparer des u128 devrait être relativement rapide pour le processeur pour parcourir l'arbre interne.
    Je vais le faire avec du 128 bits, je savais même que ça existait. Du coup ça va me faciliter le calcul de hashage et aussi la fonction de comparaison, ça va être beaucoup plus simple en un seul champ de 128 bits.

  17. #37
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 324
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 324
    Points : 4 134
    Points
    4 134
    Par défaut
    Bonjour Bousk,

    Citation Envoyé par Bousk Voir le message
    comparer des u128 devrait être relativement rapide pour le processeur pour parcourir l'arbre interne.
    En effet, si le compilateur n'est pas trop ancien, il devrait savoir utiliser les instructions SSE et AVR qui permettent de traiter directement des entiers de 128 bits (même si le gros de leur jeu d'instructions concerne du vectoriel SIMD). Les opérations sont alors du même ordre de grandeur de temps qu'en 64 bit (essentiellement comparaisons et décalages dans ce cas).

    En complément de ma proposition initiale :
    • Seule la liste triée par anagramme est à conserver en usage courant (le dictionnaire initial ne sert plus).
    • Dans le cas d'une réduction aux seuls mots ayant des anagrammes, il peut être utile de conserver, en plus, les listes complètes pour l'ajout de mots potentiels anagrammes de mots qui n'en avaient pas auparavant.


    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  18. #38
    Expert éminent sénior
    Avatar de Kannagi
    Homme Profil pro
    cyber-paléontologue
    Inscrit en
    Mai 2010
    Messages
    3 214
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : cyber-paléontologue

    Informations forums :
    Inscription : Mai 2010
    Messages : 3 214
    Points : 10 140
    Points
    10 140
    Par défaut
    Citation Envoyé par Guesset Voir le message
    En effet, si le compilateur n'est pas trop ancien, il devrait savoir utiliser les instructions SSE et AVR qui permettent de traiter directement des entiers de 128 bits (même si le gros de leur jeu d'instructions concerne du vectoriel SIMD).
    Si on parle de GCC , par défaut , il va pas utiliser les instructions AVX.
    Les instructions SSE il les utilise naturellement pour les float/double si le code est compilé en 64 bits (ce sont des instructions utilisé au lieu d'utiliser le FPU du x86 qui est très médiocre ).

  19. #39
    Expert confirmé

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 324
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 324
    Points : 4 134
    Points
    4 134
    Par défaut GCC et SIMD
    Bonjours Kannagi,

    Citation Envoyé par Kannagi Voir le message
    Si on parle de GCC , par défaut , il va pas utiliser les instructions AVX.
    Les instructions SSE il les utilise naturellement pour les float/double si le code est compilé en 64 bits (ce sont des instructions utilisé au lieu d'utiliser le FPU du x86 qui est très médiocre ).
    Dans ce cas, avec GCC (pour l'instant ), la mise en 128 bits doit mettre en poids fort les premières lettres de façon à ce que la comparaison soit la plupart du temps effective sur un simple 64 bits (pour tous les mots d'au plus 12 caractères).

    Salut.
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  20. #40
    Membre éclairé
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    261
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 261
    Points : 786
    Points
    786
    Billets dans le blog
    2
    Par défaut
    Super! Y a pas __int128 sur Visual et pourtant il met en évidence la coloration syntaxique du type.... Et apparemment on peu rien y faire!
    Bon je crois que je vais rester avec mes deux 64 bits!

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 3 PremièrePremière 123 DernièreDernière

Discussions similaires

  1. [maintenance][performance] Que faire comme maintenance ?
    Par woodwai dans le forum PostgreSQL
    Réponses: 5
    Dernier message: 06/11/2003, 15h39
  2. Performance xml
    Par MicKCanE dans le forum XML/XSL et SOAP
    Réponses: 2
    Dernier message: 07/07/2003, 06h41
  3. [ POSTGRESQL ] Problème de performance
    Par Djouls64 dans le forum PostgreSQL
    Réponses: 6
    Dernier message: 26/05/2003, 16h18
  4. [JDBC][connexion persistante] performances avec JDBC
    Par nawac dans le forum Connexion aux bases de données
    Réponses: 6
    Dernier message: 06/05/2003, 10h37
  5. performance entre 3DS, ase, asc ...
    Par amaury pouly dans le forum OpenGL
    Réponses: 3
    Dernier message: 24/03/2003, 11h41

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