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

Langage Delphi Discussion :

Y-a-t-il plus rapide pour enlever les accents ?


Sujet :

Langage Delphi

  1. #21
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    Et pour clore la discussion, je viens de faire quelques essais, et mon idée d'un CaseOf optimisé par une méthode inspirée du tri rapide ne tient pas la route. Tant pis.

    La méthode du tableau de conversion reste la plus rapide. J'en ai fait une version assembleur, mais elle n'est pas beaucoup plus rapide que la version Pascal !

    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
    // Constante à utiliser pour conversion en majuscules non accentuées.
    // Pour une simple suppression des accents, utiliser l'autre constante,
    // celle qui contient des minuscules.
    var CharTable : array[Char] of Char
                 =  #0#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 +
                    ' !"#$%&''()*+,-./0123456789:;<=>?' +
                    '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_' +
                    '`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~'#127 +
                    '€'#129'‚ƒ„…†‡ˆ‰S‹Œ'#141'Z'#143#144'‘’“”•–—˜™S›Œ'#157'ZY' +
                    #160'¡¢£¤¥¦§¨©ª«¬*®¯°±²³´µ¶·¸¹º»¼½¾¿' +
                    'AAAAAAÆCEEEEIIIIDNOOOOO×OUUUUYÞß' +
                    'AAAAAAÆCEEEEIIIIDNOOOOO÷OUUUUYÞY';
     
     
    function CJ8Buff(S:PAnsiChar;Len:Integer):PAnsiChar;register;
    asm
            PUSH    ESI // Sauvegarde ESI par nécesssité technique
            PUSH    EDI // Sauvegarde EDI par nécesssité technique
            PUSH    EBX // Sauvegarde EBX par nécesssité technique
            PUSH    EAX // Sauvegarde l'adresse de la chaîne pour retour
     
            CLD
            XOR     EBX,EBX   // EBX := 0
            CMP     EDX,EBX   // Len <= 0 ?
            JLE     @Out      // Si oui, sauter
     
            MOV     ESI,EAX   // Adresse de la chaîne dans ESI
            MOV     EDI,EAX   // Même adresse dans EDI
            MOV     ECX,EDX   // Longueur dans ECX
            LEA     EBX,CharTable // Adresse du tableau dans EBX
     
    @Loop:
            LODSB             // AL <- [ESI] ; Inc(ESI)
            XLAT              // AL <- [EBX + AL]
            STOSB             // [EDI] <- AL ; Inc(EDI)
            DEC     ECX       // On décrémente le compteur
            JNE     @Loop     // si pas fini, boucle
     
    @Out:
            POP     EAX       // renvoie dans Result l'adresse de la chaîne
            POP     EBX
            POP     EDI
            POP     ESI
    end;
     
    function CJ8(const S:string):string;
    var
      Len: Integer;
    begin
      Len := Length(S);
      SetString(Result, PChar(S), Len);
      if Len > 0 then CJ8Buff(Pointer(Result), Len);
    end;
    Edit : le MOV ECX,EDX n'est pas inutile, il est là pour pouvoir éventuellement utiliser l'instruction LOOP ; il paraîtrait que la performance gagnée/perdue par l'usage de LOOP dépend du type de processeur...

    Edit 2 : S'il y a si peu de gain de performance, c'est sans doute parce que cette routine doit être très proche de la routine CharUpperBuff de Windows, utilisée par Delphi...

  2. #22
    Membre habitué
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    624
    Détails du profil
    Informations personnelles :
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 624
    Points : 199
    Points
    199
    Par défaut
    Salut Paul,

    pour répondre à ta question :
    la procédure a-t-elle besoin d'être super optimisée, est-elle utilisée de nombreuses fois ou pas ?
    Disons que j'ai entre 800 et 1000 fichiers de texte d'environ 3 à 4 pages chacun.
    On peut dire que le corpus à traiter est assez conséquent.

    Amicalement,
    Bruno

  3. #23
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 621
    Points : 25 321
    Points
    25 321
    Par défaut
    Après tout dépend ce que tu en fais ... si c'est comme pour ce que j'ai fait c'est à dire généré un dictionnaire de mot qui permet de dire dans telle page / tel fichier, on trouve tel mot ... je ne pense pas que ce la soit les accents qui fassent prendre le plus de temps, le SQL pour mon cas était au début 99.99% du temps (fichier de 300Mo à parser en créant des arbres de hiérarchies pour les chapitres, l'examen du texte avec gestion du mot dans sa forme strict et dans sa forme Upper/NoAccent, le tout en 1 minute, traitement total 12h, après avec quelques astuces (uniquement valable pour le framework mon ancien employeur), la BD réprésentait toujours 98% du temps d'execution (toujours 1 minute pour le fichier, traitement total 1h...) ... donc vouloir optimiser c'est bien, si c'est pour gagner bcp ... car si tu as des traitements encore plus lents derrière, est-ce que cela se verra ?

    j'ai remarqué que l'on est passé de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function Bidule(S:string): string;
    à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    function Bidule(const S:string): string;
    rien que cela augmente les performances car avec "const" cela retire une copie de la chaine passée en paramètre ... et je n'ai pas remarqué que quelques avaient précisé ce fait pas forcément su de tous ...

  4. #24
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Bonjour,

    J'ai fait quelques tests supplémentaires :
    A ) Toujours dans les conditions d'essais détaillées à la fin de mon msg du 16/07/2007, 18h49 ) :
    Résultats pour la boucle de 20 000 conversions :
    - avec la fonction CJ8 + CJ8Buff() en Asm en état de marche : 14 à 15 millisec
    - avec la fonction CJ8 + CJ8Buff() réduite à "asm {} end" : 8 à 9ms
    ... en conséquence l'appel par CJ8 de CJ8Buff() prend 8 à 9 ms et le noyau Asm CJ8Buff() en état de marche ne prend que 6 ms. Donc comme l'appel est nettement plus lent que le noyau en Asm on peut se demander s'il n'y aurait pas une astuce qui permettrait de gratter encore quelques millisec en modifiant la partie Pascal de la fonction CJ8 ?
    Car jusqu'à présent le recours à l'assembler a essentiellement permis de conclure que les variantes en PChar sont au moins aussi rapides voire plus et avec nettement moins de lignes de code que ceux en Asm (bien sûr ça a aussi permis de conclure qu'on a cherché à gratter au maximum).
    B)
    Passage de "function Bidule(S:string)" à "function Bidule(const S:string)" signalé par ShaiLeTroll comme favorisant les performances :
    Comme le code d'origine de Paul Thot (version PChar) respectait déjà cette condition (dommage, ça aurait permis de gratter un peu de temps) j'ai fait quand même un test de comparaison en bouclant
    2 000 000 de fois avec la même chaîne au lieu de 20 000 :
    - fonction Paul Thot n°2 avec "const" : 1288 ms
    - fonction Paul Thot n°2 sans "const" : 1356 ms
    - ça fait quand même un gain de vitesse de 5% et avec un seul mot, bigre.
    A+

  5. #25
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    il y a un autre facteur a prendre en compte, qui est indépendant de la procédure. Je rappelle qu'il est question de 800 à 1000 fichiers de texte d'environ 3 à 4 pages chacun.

    du coup l'accès disque et la façon dont sont chargés les fichiers deviennent importants

    il sera par exemple plus efficace de placer le fichier dans un seul STRING (si la taille du fichier le permet) converti en un seul appel de la procédure, que d'appeler la procédure pour chaque ligne du fichier par exemple.

    Profiler une procédure isolée de son contexte est pédagogiquement intéressant, mais c'est oublié qu'elle n'est pas forcément la source du ralentissement général

    du coup on gagne quelque ms ici alors qu'il y a des secondes pleines à récupérer ailleurs

  6. #26
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    Déjà, tu peux supprimer le "if Len > 0 then" qui fait double usage, puisque je fais le même test dans la routine assembleur. Ensuite, ça doit beaucoup dépendre aussi des processseurs : certains AMD sont extrêmement rapides pour recopier des blocs de mémoire.

    Car il y a effectivement du temps gaspillé de cette manière : je pense qu'on peut au moins économiser une recopie de la chaîne : en effet, SetString réserve l'espace dans le Heap (ça, ça prend du temps, et on ne peut rien y faire), mais recopie ensuite la chaîne qui sera "écrasée" par les majuscules, alors qu'on pourrait très bien partir de la source directement.

    Or, une simple observation de la routine en assembleur montre qu'il suffit de rien du tout pour faire la conversion non pas de la chaîne sur elle-même, mais de la chaîne source vers la chaîne de destination. On économise alors une recopie en utilisant SetLength au lieu de SetString.

    Je n'y avais pas tout de suite pensé, voici ce que ça donne. Et je pense qu'on ne peut vraiment pas faire mieux, car le temps mis par Delphi pour réallouer de l'espace à la chaîne résultat ne pourra pas être économisé, sauf en effectuant des appels directs à la routine assembleur, la chaîne résultante étant dans un tableau de type Char fixe (ça, ça reste toujours possible).

    J'ajoute un test "chaîne vide" en amont, ça ne coûte rien mais ça économise beaucoup sur des chaînes vides (non, je ne comprends pas non plus pourquoi ! )

    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
    function CJ9Buff(const Source,Dest:PAnsiChar;const Len:Integer):PAnsiChar;register;
    asm
            PUSH    ESI // Sauvegarde ESI par nécesssité technique
            PUSH    EDI // Sauvegarde EDI par nécesssité technique
            PUSH    EBX // Sauvegarde EBX par nécesssité technique
            PUSH    EAX
     
            CLD
            XOR     EBX,EBX   // EBX := 0
            CMP     ECX,EBX   // Len <= 0 ?
            JLE     @Out      // Si oui, sauter
     
            MOV     ESI,EAX   // Adresse de la source chaîne dans ESI
            MOV     EDI,EDX   // Adresse de la destination dans EDI
            LEA     EBX,MNA   // Adresse de MNA dans EBX
     
    @Loop:
            LODSB             // AL <- [ESI] ; Inc(ESI)
            XLAT              // AL <- [EBX + AL]
            STOSB             // [EDI] <- AL ; Inc(EDI)
            DEC     ECX       // On décrémente le compteur
            JNE     @Loop     // si pas fini, boucle
     
    @Out:
            POP     EAX       // renvoie dans Result l'adresse de la chaîne
            POP     EBX
            POP     EDI
            POP     ESI
    end;
     
    function CJ9(const S:string):string;
    var
      Len: Integer;
    begin
      if S = '' then Result := '' else
       begin
        Len := Length(S);
        SetLength(Result, Len);
        CJ9Buff(PAnsiChar(S), PAnsiChar(Result), Len);
       end;
    end;
    Edit : la remarque de Paul TOTH est frappée au coin du bon sens pile et face, personnellement j'ai toujours aimé l'assembleur et ça me donne l'occasion de m'y remettre, mais il est clair qu'il faut aussi voir quelles sont les autres sources de gaspillage de temps.

  7. #27
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,
    Paul TOTH a écrit :
    ... du coup l'accès disque et la façon dont sont chargés les fichiers deviennent importants
    ... vu, cela pourrait faire l'objet d'une autre discussion.

    Paul TOTH a écrit :
    ...il sera par exemple plus efficace de placer le fichier dans un seul STRING (si la taille du fichier le permet) converti en un seul appel de la procédure, que d'appeler la procédure pour chaque ligne du fichier par exemple.
    ... ceci est effectivement une bonne parade pour réduire le rapport entre le temps-mis-pour-l'appel et le temps-de-conversion-proporement-dit et en plus c'est valable quelle que soit la fonction de conversion utilisée.

    Paul TOTH a écrit :
    Profiler une procédure isolée de son contexte est pédagogiquement intéressant, mais c'est oublié qu'elle n'est pas forcément la source du ralentissement général du coup on gagne quelque ms ici alors qu'il y a des secondes pleines à récupérer ailleurs.
    ... je dirais plutôt "faut réunir de belles perles pour faire de jolis colliers" donc tout temps gagné est gagné (la procédure initiale de Bruno13 mettait 5991 ms contre 13 ms avec la vôtre).
    ... par contre absolument d'accord de rappeler l'intérêt de connaître la source du ralentissement général. J'avais d'ailleurs déjà dit à Bruno13 que ça vaudrait le coup d'identifier au chronomètre la ou les étapes qui prennent le plus de temps vers la fin de sa discussion intitulée "[Résolu] VirtualDrawTree : checkbox qui s'affiche uniquement au passage de la souris Bruno13" suite à quoi il a ouvert la présente discussion, mais il y a probablement d'autres étapes à optimiser.
    Le problème est que ceux qui ouvrent des discussions ont une vision nettement plus globale sur leur problème que ceux qui répondent et se trouvent cantonnés à regarder au travers d'un trou de serrure sur un aspect particulier du problème ... par contre ça permet de régler un sous-problème après l'autre avec l'inconvénient de ne les découvrir que l'un après l'autre ... mais "faut faire avec" comme on dit.
    A+
    EDIT : je viens juste de découvrir que le post de CapJack s'est intercalé devant le mien.

  8. #28
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,

    J'ai fait deux essais différents avec la dernière fonction CJ9 de CapJack :
    - 1) Dans les conditions d'essais de mon msg du 16/07/2007, 18h49 donc avec 20 000 appels à CJ9(LigAccents) : 12,34 à 13,08 ms.
    - 2) Avec un seul appel unique à CJ9(SL.text) où la StringList SL a été chargée au préalable hors chronomètrage avec 20000 do SL.Add(LigAccents) : 25,44 à 27,28 ms et là je n'y comprends plus rien !!!???.
    A toutes fins utiles voiçi le code du test :
    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
    procedure TForm1.bCJack9Click(Sender: TObject);
    var       s : string; i : integer; Chrono : oChrono; SL : TstringList;
    begin     RichEdit1.clear;
              SL := TstringList.create;
              for i:=1 to 20000 do SL.Add(LigAccents);
              Application.ProcessMessages;
              Chrono.Top;
              //for i:=1 to 20000 do s:=CJ9(LigAccents);
              s:=CJ9(SL.text);
              labChrono.caption:='Mis : '+FloatToStrf(Chrono.Mis,ffFixed,10,2)+' ms';
              // mis 12,34 à 13,08 ms avec 20 000 appels à CJ9(LigAccents)
              // mis 25,44 à 27,28 ms avec un seul appel à CJ9(SL.text);
              RichEdit1.lines.add(s);
    end;
    A+

  9. #29
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    Citation Envoyé par Gilbert Geyer
    Re-bonjour,

    J'ai fait deux essais différents avec la fonction CJ9 :
    - 1) Dans les conditions d'essais de mon msg du 16/07/2007, 18h49 donc avec 20 000 appels à CJ9(LigAccents) : 12,34 à 13,08 ms.
    - 2) Avec un seul appel unique à CJ9(SL.text) où la StringList SL a été chargée au préalable hors chronomètrage avec 20000 do SL.Add(LigAccents) : 25,44 à 27,28 ms et là je n'y comprends plus rien !!!???.
    A toutes fins utiles voiçi le code du test :
    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
    procedure TForm1.bCJack9Click(Sender: TObject);
    var       s : string; i : integer; Chrono : oChrono; SL : TstringList;
    begin     RichEdit1.clear;
              SL := TstringList.create;
              for i:=1 to 20000 do SL.Add(LigAccents);
              Application.ProcessMessages;
              Chrono.Top;
              //for i:=1 to 20000 do s:=CJ9(LigAccents);
              s:=CJ9(SL.text);
              labChrono.caption:='Mis : '+FloatToStrf(Chrono.Mis,ffFixed,10,2)+' ms';
              // mis 12,34 à 13,08 ms avec 20 000 appels à CJ9(LigAccents)
              // mis 25,44 à 27,28 ms avec un seul appel à CJ9(SL.text);
              RichEdit1.lines.add(s);
    end;
    A+
    attention, SL.Text va prendre du temps

    place un s:=SL.Text en dehors du chrono

    tu peux aussi charger le fichier directement dans un string
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    function LoadFile(const AFileName:string):string;
    var
     s:TStream;
    begin
     s:=TFileStream.Create(AFileNAme,fmOpenRead);
     SetLength(Result,s.Size);
     s.ReadBuffer(Result[1],s.Size);
     s.Free;
    end;

  10. #30
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,

    Paul TOTH a écrit :
    attention, SL.Text va prendre du temps
    place un s:=SL.Text en dehors du chrono
    ... c'est effectivement mieux, avec un seul appel à s2:=CJ9(s1) on descend entre 8,48 à 9,28 ms (mais 1 fois sur dix 13,42 ms). MAis je crois que le 13,42 ms qui revient de temps à autre provient du fait que Windows doit reprendre la main de temps à autre malgré le Application.ProcessMessages que j'avais placé devant le Chrono.Top en espérant que cela stabiliserait les différences de résultats d'un click au suivant.
    Par contre la descente vers 8,48 à 9,28 ms confirme l'intérêt de réduire pour un texte chaque fois que possible les appels à un appel unique.

    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
    procedure TForm1.bCJack9Click(Sender: TObject);
    var       s1,s2 : string; i : integer; Chrono : oChrono; SL : TstringList;
    begin     RichEdit1.clear;
              SL := TstringList.create;
              for i:=1 to 20000 do SL.Add(LigAccents);
              s1:=SL.text;
              Application.ProcessMessages;
              Chrono.Top;
              //for i:=1 to 20000 do s2:=CJ9(LigAccents);
              s2:=CJ9(s1);
              labChrono.caption:='Mis : '+FloatToStrf(Chrono.Mis,ffFixed,10,2)+' ms';
              // mis 12,34 à 13,08 ms avec 20 000 appels à CJ9(LigAccents)
              // mis 25,44 à 27,28 ms avec un seul appel à CJ9(SL.text);
              // mis  8,48 à  9,28 ms (mais 1 fois sur dix 13,42 ms) avec un seul appel à s2:=CJ9(s1);
              RichEdit1.lines.add(s2);
    end;
    Mais du coup je vais également utiliser le même protocole d'essais avec la version PChar car si on gagne la même chose celle-ci a l'avantage d'utiliser moins de lignes de code.

    P.S : Je n'ai pas utilisé la function LoadFile(const AFileName:string):string; vu que le temps de création de s1:=SL.text est ici hors chrono, mais elle pourra servir pour optimiser d'autres étapes le cas échéant.
    A+

  11. #31
    Membre habitué
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    624
    Détails du profil
    Informations personnelles :
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 624
    Points : 199
    Points
    199
    Par défaut
    Bonjour à tous,

    Pour répondre un peu dans le désordre à toutes les questions que vous vous posez, voici quelques infos :

    Vision globale du projet :
    - Les textes sont récupérées via le net donc du HTML et je ne connais pas leur contenu.
    - Conversion HTML2Txt
    - Détecter la langue de chaque fichiers
    - Suivant la langue trouvée, je vire les mots vides grâce à un fichier correspondant contenant la liste des mots à virer.
    - Je vire les accents
    - Je calcule pour chaque page en respectant la langue : la Frequence, l'occurrence de chaque mot et combinaison de mots à 2 formes, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Formes                   Freq          Occur
    veille                    10               3
    veille technologique       7               2
    technologique              8               2
    Comme vous l'avez si bien dit, il vous faut une vision globale pour bien cerner le problème.

    Je charge mes fichiers dans une String, et ce que je peux dire en ce moment
    c'est que ma phase "Accent" est ok.

    Je fais maintenant mes essais sur la page :
    http://www.american.edu/ia/cfer/report/report.html

    Et j'ai un peu plus de deux minutes pour :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    PR.PageCleared:=DeleteEmptyWord(PR.PageProcess, s);
    // s etant la langue ex: english pour ensuite recuperer le fichier 
    // english.stw  (stopwords)
    Je vais donc comme à mon habitude ouvrir un petit post pour essayer de résoudre ce problème et gagner en rapidité car il me reste plus que cela dans mon traitement à optimiser.


    En tous les cas, merci à tous pour vos conseils.

    Amicalement,
    Bruno

  12. #32
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 621
    Points : 25 321
    Points
    25 321
    Par défaut
    Citation Envoyé par Gilbert Geyer
    Windows doit reprendre la main de temps à autre malgré le Application.ProcessMessages
    le ProcessMessages a une fonction bien particulière il va aspirer dans sa liste des messages ceux qui lui sont addressé et les traiter, cela va d'un event de socket bloquant au dessin d'un bouton ... ceci est couteux en temps et totalement irrégulier ... d'ailleurs, windows, ne se gène pas à ce moment pour passer la main à d'autres applications, mais il faut rappeler que windows est multi tache, lance deux programmes qui feront une boucle pendant 1 minutes, chacun leur tour, ils auront leurs chances pour bosser, heureusement d'ailleurs, mais bien sur, occuper le processeurs avec de tel boucle, ralenti la machine car ces processus ne relache jamais la main par eux même (un thread contient souvent un sleep dans sa boucle while not terminated, et bien la boucle Run de l'Application c'est exactement la même chose, elle relache aussi la main volontairement, et si besoin, window lui se permet de piquer la main pour les autres process ...)

  13. #33
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,

    1) Info suite à discussion avec Paul Thot : j'ai utilisé le même protocole d'essai évuqué au sujet de CJ9 sur la version PChar avec s1:=SL.text hors chrono : les temps sont pratiquement inchangés que l'on appelle la foncction 20 000 fois ou que l'on l'appelle une seule fois avec s1 qui contient les 20 000 lignes. Ce n'est donc que CJ9 qui est sensible au nombre d'aappels.

    2) Questions pour Bruno13 :
    a) Tu dis "- Je vire les accents" : Pour mieux piger, pourquoi virer uniquement les accents plutôt qu'une conversion intégrale en majuscules non accentuées qui prend le même temps sachant en plus qu'un même mot peut apparaître dans un même texte tantôt en majuscules tantôt en minuscules ce qui risque de fausser le comptage des fréquences ?
    b) Et les carcatères de ponctuation qui restent accolés aux mots dans le genre mot,;. et qui donc faussent les retours de IndexOfSL3(SL : TStringList; const S: string): Integer tu les as aussi virés ?
    ... pour le reste, suite au prochain post que tu annonces ouvrir pour le problème suivant.

    3) A ShaiLeTroll : Pigé ces subtilités de fonctionnement de Windows : il pique la main et en plus de façon totalement irrégulière !
    .. J'avais un jour tenté ma chance avec SetPriorityClass(hProcess, PriorityClass) pour lui demander de me laisser tranquille un instant pour une une boucle à executer immédiatement et ça n'y a rien changé non plus, mais c'est peut-être moi qui m'y suis mal pris.
    A+

  14. #34
    Membre habitué
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    624
    Détails du profil
    Informations personnelles :
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 624
    Points : 199
    Points
    199
    Par défaut
    Salut Gilbert,

    a) Tu dis "- Je vire les accents" : Pour mieux piger, pourquoi virer uniquement les accents plutôt qu'une conversion intégrale en majuscules non accentuées qui prend le même temps sachant en plus qu'un même mot peut apparaître dans un même texte tantôt en majuscules tantôt en minuscules ce qui risque de fausser le comptage des fréquences ?
    Parce qu'en fait, lors de l'utilisation de ma fonction HTMLToTxt, je convertie le tout en minuscule. Et ceci est trés rapide donc pas besoin d'y toucher.


    b) Et les carcatères de ponctuation qui restent accolés aux mots dans le genre mot,;. et qui donc faussent les retours de IndexOfSL3(SL : TStringList; const S: string): Integer tu les as aussi virés ?
    ... pour le reste, suite au prochain post que tu annonces ouvrir pour le problème suivant.
    Oui j'ai aussi une fonction qui vire les ponctuations

    Pour tout vous dire, le prochain post sera sur la suppression des mots vides car c'est vraiment elle qui prend bcp bcp de temps finalement.

    Suite dans le nouveau Post

    Amicalement,
    Bruno

  15. #35
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Salut Bruno,


    Juste une remarque : Tu dis "Oui j'ai aussi une fonction qui vire les ponctuations". Et comme à cette fonction s'ajoute celle qui vire les accents pourquoi ne pas virer les ponctuations et les accents avec une seule fonction c'est à dire celle qui vire les accents il suffit à cet effet, dans la constante utilisée pour la conversion des 256 caractères, de remplacer les caractères .,;:! par des espaces ' ' ... et hop ça vire les ponctuations et les accents en un seul et même temps ... (sauf si une autre logique t'impose de faire ces deux opérations à des moments différents).

    Au fait t'as retenu quelle fonction pour la suppression des accents, celle basée sur les PChar ou CJ9 avec le noyau en assembleur ?

    Pour la suppression des mots vides qui prend bcp bcp de temps suite dans ton nouveau Post.

    Amicalement.

  16. #36
    Membre habitué
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    624
    Détails du profil
    Informations personnelles :
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 624
    Points : 199
    Points
    199
    Par défaut
    Salut Gilbert,

    j'ai retenu une des premieres fonctions que vous avez posté, bien avant celle en assembleur. C'est EnleveAccentsCaseOfDoubles.

    En tout cas mille merci pour tous tes/vos conseils et sources.

    Amicalement,
    Bruno

  17. #37
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-Salut Bruno,

    Tu dis "bien avant celle en assembleur" : il y en a plusieurs en assembleur en excluant la mienne qui est la plus lente et dont la longueur du code est excessive.

    Je les ai toutes testées et j'en préfère deux pour mes usages futurs :
    - celle de Paul Thot avec les Pchar (rapport vitesse/nombre de lignes de codes=maxi, pas de piège lors de l'utilisation)
    - et celle en Asm de CapJack (function CJ9) qui est plus rapide que celle de Paul Thot (dans un rapport de 13/9 environ) sous réserve :
    1) d'apeller la function CJ9 en une seule fois pour la totalité d'un texte au-lieu de l'appeler dans une boucle ligne par ligne (sinon elle n'est guère plus rapide que celle de Paul Thot)
    2) et sous réserve d'éviter le piège de l'appeler avec une ligne de code du style s2:=CJ9(SLText.Text) ce qui aurait pour effet de la rendre environ deux fois plus lente que celle de Paul Thot, sachant que pour éviter ceci il faut faire s1:=SLText.Text et ensuite seulement s2:=CJ9(s1) pour bénéficier du gain de vitesse espéré.

    En plus comme ces deux fonctions utilisent les constantes de conversion des 256 caractères on peut les adapter facilement pour faire des conversions en tous genres : avec ou sans accents, majuscules et/ou minuscules, remplacement de caractères indésirables (,;:!) par ' ' etc.
    A+

  18. #38
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut Faq
    Bonjour,

    Au fait cela vaudrait le coup de proposer les deux meilleures fonctions de conversion issues de cette discussion pour alimenter la
    A+

  19. #39
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    Je me permets de déterrer ce fil, car je n'ai jamais vraiment laissé tomber l'idée de gérer correctement les doubles voyelles ae et oe. C'est chose faite aujourd'hui, j'ai eu un moment pour y penser. Le problème de base était que l'utilisation de Insert conduirait à des décalages de blocs très consommateurs de temps. J'ai donc adopté une autre démarche.

    Le temps de calcul sur la chaîne complète est à peine doublé par rapport au MNA "simple" (+80%), ce qui compte-tenu du fait que la chaîne résultante peut être plus longue que la chaîne de départ, est déjà en soi une performance.

    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
    var MNA : array[Char] of Char
                 =  #0#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 +
                    ' !"#$%&''()*+,-./0123456789:;<=>?' +
                    '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_' +
                    '`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~'#127 +
                    '€'#129'‚ƒ„…†‡ˆ‰S‹Œ'#141'Z'#143#144'‘’“”•–—˜™S›Œ'#157'ZY' +
                    #160'¡¢£¤¥¦§¨©ª«¬*®¯°±²³´µ¶·¸¹º»¼½¾¿' +
                    'AAAAAAÆCEEEEIIIIDNOOOOO×OUUUUYÞß' +
                    'AAAAAAÆCEEEEIIIIDNOOOOO÷OUUUUYÞY';
     
    // ATTENTION !
    // Cette fonction ne renvoie pas un pointeur sur la chaîne résultante,
    // mais la LONGUEUR de la chaîne résultante.
    Function  DTAnsiConvertMNA2
            (
            const Source            : PAnsiChar;
            const Dest              : PAnsiChar;
            const Len               : Integer
            ):Integer;register;
    asm
            PUSH    ESI // Sauvegarde ESI par nécesssité technique
            PUSH    EDI // Sauvegarde EDI par nécesssité technique
            PUSH    EBX // Sauvegarde EBX par nécesssité technique
     
            CLD
            MOV     EDI,EDX   // Adresse de la destination dans EDI
            XOR     EBX,EBX   // EBX := 0
            CMP     ECX,EBX   // Len <= 0 ?
            JLE     @@Out     // Si oui, sauter
     
            MOV     ESI,EAX   // Adresse de la source chaîne dans ESI
            LEA     EBX,MNA   // Adresse de MNA dans EBX
     
     
    @@Loop:
            LODSB             // AL <- [ESI] ; Inc(ESI)
            XLAT              // AL <- [EBX + AL]
            CMP     AL,198    // Est-ce un ae collé majuscule ? Æ
            JZ      @@LetAE
            CMP     AL,140    // Est-ce un oe collé majuscule ? Œ
            JZ      @@LetOE
    @@Next:
            STOSB             // [EDI] <- AL ; Inc(EDI)
            DEC     ECX       // On décrémente le compteur
            JNE     @@Loop    // si pas fini, boucle
            JMP     @@Out
     
    @@LetAE:
            MOV     AL,'A';   // Caractère A...
            STOSB             // [EDI] <- AL ; Inc(EDI)
            MOV     AL,'E'    // ... puis caractère E
            JMP     @@Next
     
    @@LetOE:
            MOV     AL,'O';   // Caractère O...
            STOSB             // [EDI] <- AL ; Inc(EDI)
            MOV     AL,'E'    // ... puis caractère E
            JMP     @@Next
     
    @@Out:
            MOV     EAX,EDI
            SUB     EAX,EDX   // calcule la longueur de la chaîne résultante
            POP     EBX
            POP     EDI
            POP     ESI
     
    end;
     
    Function  DTConvertMNA2
            (
            const Source            : string
            ):string;
    var
      Len: Integer;
    begin
      if Source = '' then Result := '' else
       begin
        Len := Length(Source);
        SetLength ( Result , Len + Len + 1 ); // Prévoyons de la place...
        Len := DTAnsiConvertMNA2(PAnsiChar(Source), PAnsiChar(Result), Len);
        SetLength ( Result , Len ); // ... puis ajustons la longueur
       end;
    end;
    Bonne soirée à tous.

  20. #40
    Candidat au Club
    Homme Profil pro
    Retraité
    Inscrit en
    Mai 2023
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 72
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Retraité
    Secteur : Enseignement

    Informations forums :
    Inscription : Mai 2023
    Messages : 3
    Points : 3
    Points
    3
    Par défaut Convertir les caratcère diacritiques
    Citation Envoyé par Bruno13 Voir le message
    Bonjour,

    quelqu'un aurait-il une fonction/procedure pour supprimer les accents plus rapide que celle-ci que j'ai trouvé sur ce forum ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function EnleveAccents(AText : String) : string;
    const 
      Char_Accents      = 'ÀÁÂÃÄÅàáâãäåÒÓÔÕÖØòóôõöøÈÉÊËèéêëÇçÌÍÎÏìíîïÙÚÛÜùúûüÿÑñ';
      Char_Sans_Accents = 'AAAAAAaaaaaaOOOOOOooooooEEEEeeeeCcIIIIiiiiUUUUuuuuyNn';
    ...
    Amicalement,
    Bruno
    Je pleure en voyant les différentes réponses et tentatives.

    Avec l'exemple ci dessus on peut faire (en ASCII) :

    // Pour les caractères dacritiques
    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
     
    function SansAccent(WCh : WideChar): WideChar; assembler;
    const
      Char_Accents      : PWideChar = 'ÀÁÂÃÄÅàáâãäåÒÓÔÕÖØòóôõöøÈÉÊËèéêëÇçÌÍÎÏìíîïÙÚÛÜùúûüÿÑñ';
      Char_Sans_Accents : PWideChar = 'AAAAAAaaaaaaOOOOOOooooooEEEEeeeeCcIIIIiiiiUUUUuuuuyNn';
    asm
           OR      AX, AX
           JE      @@2
           MOV     ECX, 53
           PUSH    EDI
           MOV     EDI, Char_Accents
           REPNE   SCASW      // Recherche le caractère
           JNE     @@1        // Pas trouvé
           SUB     EDI, Char_Accents
           ADD     EDI, Char_Sans_Accents
           MOV     AX, [EDI - 2]
    @@1:   POP     EDI
    @@2:
    end;
    Et avec les caractères UNICODES, il suffit de créer un tableau de 65536 caractères type TUnicodeChar

    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
     
      TCategory = (KNulle, Lu,Ll,Lt,Lm,Lo,Mn,Mc,Me,Nd,Nl,No,Pc,Pd,Ps,Pe,Pi,Pf,Po,Sm,Sc,Sk,So,Zs,Zl,Zp,Cc,Cf,Cs,Co,Cn);
     
      TUnicodeChar = record   
        Category  : TCategory;  // Type de caractère   Byte
        Langue    : TLanguage;  // Langue              Byte
        LowCase   : WideChar;   // Minuscule           Word
        UpCase    : WideChar;   // Majuscules          Word
        Standard  : WideChar;   // Non diacritique ('éàèù' = 'eaeu')
      end;
     
      TUnicodesChars = array[0..$FFFF] of TUnicodeChar;  // Table des unicodes Minuscules et codes caractères
     
    var
      UnicodesChars : TUnicodesChars;
    Ce tableau de tous les unicodes peut être téléchargé ici : https://www.unicode.org/Public/UNIDATA/UnicodeData.txt
    Pour être ensuite convertis en binaire et mis dans un fichier resource (.RES) de 512 Ko en RT_RCDATA.

    A l'initialisation on charge le tableau comme suit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    procedure LoadUnicodesTable;
    const
      CodesChar = 110;
    var
      ResInstance: THandle;
      hResData : THandle;
    begin
      ResInstance:= FindResourceHInstance(HInstance);
      hResData := LoadResource(ResInstance, FindResource(ResInstance, MakeIntResource(CodesChar), RT_RCDATA));
      UnicodesChars := TUnicodesChars(LockResource(hResData)^);
      FreeResource(hResData);  // Seulement actif pour les versions Windows 9x
      FreeLibrary(ResInstance);
    end;
    Ensuite pour convertir en minuscule, majuscule ou non diacritique il suffit d'écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    function Minuscule(WCh: WideChar): WideChar;
    begin
      Result := UnicodesChars[Word(WCh)].LowCase;
    end;
    Y aurait-il plus rapide ?

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

Discussions similaires

  1. Y-a-t-il plus rapide pour enlever les mots vides ?
    Par Bruno13 dans le forum Delphi
    Réponses: 33
    Dernier message: 26/07/2007, 17h03
  2. [XHTML] Moyen plus rapide pour mettre mes pages en XHTML
    Par Linoa dans le forum Balisage (X)HTML et validation W3C
    Réponses: 6
    Dernier message: 30/08/2005, 17h46
  3. Algo le plus rapide pour trouver une répétition ?
    Par AsmCode dans le forum Algorithmes et structures de données
    Réponses: 3
    Dernier message: 28/07/2005, 00h26
  4. Réponses: 16
    Dernier message: 19/05/2005, 16h20
  5. [FoxPro]Instruction pour enlever les accents ?
    Par Fab-FoxPro dans le forum Autres SGBD
    Réponses: 2
    Dernier message: 19/08/2003, 15h46

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