OK
AJAX error!

Les forumsGrammalectePour avoir un gain de performance dans la version JS

Pour avoir un gain de performance dans la version JS

Bonjour,

J'ai trouvé un moyen pour gagner en performance. En regardant avec l'outil de développement des navigateur on s'aperçois qu'il y a beaucoup de temps (cumuler) qui est passé dans la fonction _convBytesToIntegerArc. Il est assez facile de le réduire !

Donc a l'initialisation de chaque dictionnaire nous avons :

let lTemp = [];
for (let i = 0; i < this.sByDic.length; i+=2) {
lTemp.push(parseInt(this.sByDic.slice(i, i+2), 16));
}
this.byDic = lTemp;


Qui permet de convertir de nouveaux en une suite de "bytes", nous pouvons donc remplacé cette initialisation en faisant la conversion avec un petit accumulateur ce qui deviendrait :


let acc = 0;
let acc_byte = [];
let lTemp = [];
for (let i = 0; i < this.sByDic.length; i+=2) {
acc_byte.push(parseInt(this.sByDic.slice(i, i+2), 16));
if (acc == (this.nBytesArc - 1)) {
lTemp.push(this._convBytesToInteger(acc_byte));
acc_byte = [];
} else if (acc == (this.nBytesArc + this.nBytesNodeAddress - 1)) {
lTemp.push(this._convBytesToInteger(acc_byte) / this.nBytesNodeAddress);
acc_byte = [];
acc = -1;
}
acc = acc + 1;
}
this.byDic = lTemp;



Après évidement il faut adapté les autres fonction :


_lookupArcNode1 (nVal, iAddr) {
// looks if nVal is an arc at the node at iAddr, if yes, returns address of next node else None
while (true) {
let iEndArcAddr = iAddr+1;
let nRawArc = this.byDic[iAddr];
if (nVal == (nRawArc & this._arcMask)) {
// the value we are looking for
// we return the address of the next node
return this.byDic[iEndArcAddr];
}
else {
// value not found
if (nRawArc & this._lastArcMask) {
return null;
}
iAddr = iEndArcAddr + 1;
}
}
}

lookup (sWord) {
// returns true if sWord in dictionary (strict verification)
let iAddr = 0;
for (let c of sWord) {
let dCharTmp = this.dChar.get(c);
if (!dCharTmp) {
return false;
}
iAddr = this._lookupArcNode(dCharTmp, iAddr);
if (iAddr === null) {
return false;
}
}
return Boolean(this.byDic[iAddr] & this._finalNodeMask);
}
...


Avec ses modification nous avons déjà lookup qui fonctionne et donc sur ma machine pour 20 000 lookup de :
"salut" 70ms -> 13ms
"gfb" 62ms -> 11ms
"anticonstitutionnellement" 128ms -> 26ms

Pas fait les conversions de code pour la suite mais je peux vous le faire... Je pense que la perte de performance au démarrage est négligeable je n'ai pas mesuré. Si j'ai pas fait d’erreur ça fait un sacré gain !!!

Voilà !
le 11 septembre 2020 à 13:36
Si j'ai rien oublié => www.diffchecker.com/U3YeYDrG
Par contre le diff est basé sur le fichier généré ;)

AVANT

Vérification de: salut itération: 20000 -> true
Fonction #salut: 64.13818359375 ms
Vérification de: gfb itération: 20000 -> false
Fonction #gfb: 60.38330078125 ms
Vérification de: anticonstitutionnellement itération: 20000 -> true
Fonction #anticonstitutionnellement: 134.945068359375 ms
----------------------------------
Morph de: suis itération: 20000 -> (3) [">suivre/:V3_it_x__a:E:2s/*", ">suivre/:V3_it_x__a:Ip:1s:2s/*", ">être/:V0ei_____a:Ip:1s/*"]
Morph #suis: 76.57177734375 ms
Morph de: salut itération: 20000 -> [">salut/:N:m:s/*"]
Morph #salut: 56.389892578125 ms
Morph de: salutez itération: 20000 -> []
Morph #salutez: 63.0849609375 ms
----------------------------------
Stem de: suis itération: 20000 -> (2) ["suivre", "être"]
Stem #suis: 52.719970703125 ms
Stem de: salut itération: 20000 -> ["salut"]
Stem #salut: 58.478759765625 ms
Stem de: salutez itération: 20000 -> []
Stem #salutez: 63.947021484375 ms
----------------------------------



APRES

Vérification de: salut itération: 20000 -> true
Fonction #salut: 14.5869140625 ms
Vérification de: gfb itération: 20000 -> false
Fonction #gfb: 9.135009765625 ms
Vérification de: anticonstitutionnellement itération: 20000 -> true
Fonction #anticonstitutionnellement: 25.699951171875 ms
----------------------------------
Morph de: suis itération: 20000 -> (3) [">suivre/:V3_it_x__a:E:2s/*", ">suivre/:V3_it_x__a:Ip:1s:2s/*", ">être/:V0ei_____a:Ip:1s/*"]
Morph #suis: 32.89501953125 ms
Morph de: salut itération: 20000 -> [">salut/:N:m:s/*"]
Morph #salut: 19.31689453125 ms
Morph de: salutez itération: 20000 -> []
Morph #salutez: 19.473876953125 ms
----------------------------------
Stem de: suis itération: 20000 -> (2) ["suivre", "être"]
Stem #suis: 22.076171875 ms
Stem de: salut itération: 20000 -> ["salut"]
Stem #salut: 16.291015625 ms
Stem de: salutez itération: 20000 -> []
Stem #salutez: 17.784912109375 ms
----------------------------------

le 11 septembre 2020 à 14:41
Bonjour,

Ça a l’air intéressant, mais j’avoue que ce que vous faites au début me laisse perplexe (mais ça fait au moins cinq ans que je n’ai pas mis le nez dans cet algo assez complexe, alors j’ai oublié pas mal de détails d’implémentation).

Va falloir que je regarde ça à tête reposée.
le 11 septembre 2020 à 14:47
L’idée de faire toutes les conversions au commencement, plutôt que de les faire à chaque demande, est très bonne. J’aurais dû y penser.
En revanche, dans l’implémentation, je suis perplexe.

Il y a un truc qui me chiffonne.
Êtes-vous certain que ça fonctionne quelque soit la longueur de nBytesArc et nBytesNodeAddress ???
N’oubliez pas que les utilisateurs peuvent faire leur propre dictionnaire : ces valeurs sont donc susceptibles de changer.

La ligne qui me dérange c’est :

lTemp.push(this._convBytesToInteger(acc_byte) / this.nBytesNodeAddress);


Ici, vous convertissez l’adresse dans le dictionnaire binaire en la divisant par nBytesNodeAddress…
Si ça fonctionne tel quel, ça ressemble à une fabuleuse coïncidence, à moins que quelque subtilité m’échappe.
le 11 septembre 2020 à 15:04
Pas de problème...

C'est claire que c'est une partie assez compliqué dût essentiellement au limite de Firefox.

Attends un peu, apparemment il y a un petit bug pour le dictionnaire allvars c'est ok mais les autres posent problème... Je regarde ça puis je reviens ici ;)
le 11 septembre 2020 à 15:04
C'est effectivement la ligne que tu signale qui pose problème ;)
le 11 septembre 2020 à 15:13
Cette ligne est censée transformer une adresse du dictionnaire binaire en une adresse du tableau dont tu changes la taille, la division me semble totalement inappropriée. Il faut tout recalculer, en fait.
le 11 septembre 2020 à 15:20
Fix de la ligne pour que ça fonctionne sur tous les dico

lTemp.push(this._convBytesToInteger(acc_byte) / ((this.nBytesArc + this.nBytesNodeAddress)/2));

le 11 septembre 2020 à 15:25

Cette ligne est censée transformer une adresse du dictionnaire binaire en une adresse du tableau dont tu changes la taille, la division me semble totalement inappropriée. Il faut tout recalculer, en fait.



C'est justement le but de cette ligne de recalculé le nouvel emplacement dans le tableau... donc avec le fix de la ligne autant mettre

(this.nBytesArc + this.nBytesNodeAddress)/2

dans une variable avant la boucle pour évité de devoir la refaire dans la boucle.
le 11 septembre 2020 à 15:34
Explication:

Chaque node dans le fichier json prend comme taille this.nBytesArc + this.nBytesNodeAddress.

Notre tableau comprend pour chaque item l'arc et l'adresse donc il est (this.nBytesArc + this.nBytesNodeAddress)/2 plus petit ;) donc en divisant l'adresse du json par cette valeur nous obtenons la nouvelle adresse...


Je ne suis pas sûre d'être claire dans mon explication :s
le 11 septembre 2020 à 15:50
Cette conversion me semble un peu trop facile pour être vraie.
Mais faut vérifier.
le 11 septembre 2020 à 15:50
Si nBytesArc == 2 et si nBytesNodeAddress == 3, ça divise par 2,5. J’espère qu’il n’y a jamais de problème d’arrondis en JS.
Sinon, réflexion faite, ça semble correct.
le 11 septembre 2020 à 16:10
J'ai testé avec les 3 dictionnaires et ça fonctionne...
Ça aurait été une division par 3 ça aurait effectivement posé des problèmes avec les arrondis, mais par 2 jamais rencontrer de problèmes.
le 11 septembre 2020 à 16:12
Il faudrait plutôt tester avec des dictionnaires personnels qui ont des valeurs différentes pour nBytesArc et nBytesNodeAddress, parce que les 3 dicos inclus ont probablement les mêmes valeurs.
le 11 septembre 2020 à 16:19
Si tu as des dico perso sous la main je peux tester...

Dans les dico inclus allvars a des valeurs différentes les deux sont = 3 d'ou que ça fonctionnais pour se dico ma première versions ;)

Si une division par 2 en javascript pose problème il faut quand même s'inquiété lol
le 11 septembre 2020 à 16:23
Ce n’est pas comme si ce langage fonctionnait toujours proprement sur des choses élémentaires…

Ce peut être une division par 1,5 ou 2,5 ou 3,5, etc. Faut être sûr que ça ne renvoie pas un entier et pas un truc comme 1,999999999999 ou 2,00000000000001… À mon avis, un Math.round() pour sécuriser ça n’est pas de trop.
le 11 septembre 2020 à 16:28
Quelque soit le valeur de nBytesArc et nBytesNodeAddress nous avons une division qui donne une valeur fini vu que nous avons juste le résultat du division par 2. Autant ça aurait été par 3 des problèmes d'arrondi pourraient se produire mais pas dans notre cas.
le 11 septembre 2020 à 16:35
Les valeurs que nous divisons sont forcement des multiplicateurs entiers de la division, Quand (nBytesArc + nBytesNodeAddress)/2 donne quelque chose sous la forme x.5 nous auront forcement un nombre qui peut être divisé... vu qu'il vient de l'adresse dans le json.
le 11 septembre 2020 à 16:54
Oui, bien sûr, mais avec JS, qui peut être sûr que ça ne va pas nous péter à la gueule un jour ou l’autre ? Avec un entier divisé par un float, je ne serais quand même pas surpris si JS nous renvoyait un float de temps en temps. :) Je n’ai tout simplement aucune confiance en ce langage. Trop de merde, trop de bugs, je suis vacciné.
Du reste, ça ne coûte rien à faire, vu qu’on peut calculer cette valeur une fois au début. Ça n’a aucune importance.
le 11 septembre 2020 à 17:09
Comme tu veux !
Veux tu que je t'envoie donc un diff comme précédemment avec la ligne corrigé et l'ajout du math.round ?
le 11 septembre 2020 à 17:14
Envoie-moi un diff pour JS.
Je m’occupe de la version Python.
le 11 septembre 2020 à 17:15
C'est envoyé par email ! J'ai envoyé le nouveau fichier ;)
le 11 septembre 2020 à 17:30
Faut pas que je dorme lol
Se matin au réveil, j'ai pensé qu'on peut même aller un peu plus loin. Et donc au chargement carrément reconstituer une map de la forme :
byDic[adresse] = {nArc, fArc, fNode}
Se qui permettrait de simplifié le code !
Je vais de se pas faire des essais ;)
le 12 septembre 2020 à 10:17
Oui, absolument, j’ai pensé à la même chose, surtout que c’est déjà ainsi que sont constitués les graphes de tokens du correcteur grammatical.
L’idéal serait même de le faire directement à la création, plutôt que d’avoir à le reconstituer après sa lecture au format binaire.
Mais à mon avis, ça va bouffer une mémoire considérable. Il y a un nombre gigantesque de nodes et d’arcs.

L’idée d’en faire un dictionnaire binaire (en 2012) reposait sur la volonté de réduire au maximum la taille du dictionnaire, mais cet impératif n’a plus vraiment cours, même si j’aimerais éviter d’en faire un gigantesque blob qui bouffe une mémoire folle.
Du coup, pour l’instant, je pense que ça va rester tel quel un petit moment, parce qu’en plus j’aimerais clarifier le code et probablement virer le format totalement binaire utilisé en Python pour le remplacer par du JSON, comme pour JavaScript, histoire de se simplifier la vie avec un seul format.
le 12 septembre 2020 à 12:50
Le resultat de mes essais ne sont pas concluant, j'ai donc assez facilement obtenu un tableau ou il les resultats des bitand pour savoir si c'est un _arcMask, _lastArcMask et _finalNodeMask. Cependant j'ai modifié que pour que le lookup fonctionne et contrairement a ce que je pensais c'est plus lent :s

Je me suis souvent demandé pourquoi, il y avait plusieurs formats ;) Ne gardé que le format json me semble bien en plus ça fera gagner du temps au build du dictionnaire ;)
J'ai une petite question dans ton tableau de mesure de performance les colonnes correspondent à quelle mesure ?

Le plus gros manque en performance reste la suggestion qui est assez gourmande mais là je ne crois pas qu'il y ai de solution miracle.

Sinon c'est un peux hors sujet mais les


if (!(this.sHeader.startsWith("/grammalecte-fsa/") || this.sHeader.startsWith("/pyfsa/"))) {
throw TypeError("# Error. Not a grammalecte-fsa binary dictionary. Header: " + this.sHeader);
}
if (!(this.nCompressionMethod == 1 || this.nCompressionMethod == 2 || this.nCompressionMethod == 3)) {
throw RangeError("# Error. Unknown dictionary compression method: " + this.nCompressionMethod);
}


devraient plutôt être placé avant qu'on essaie de convertir les dicos ;)
le 12 septembre 2020 à 13:51
Il y a plusieurs formats parce que ça a d’abord été bâti en Python, avec l’idée de faire un dictionnaire compressé et rapide.
Mais en JavaScript, lire des fichiers binaires, ça avait l’air compliqué et fumeux, j’avais beaucoup galéré avec ça. C’est pourquoi je me suis rabattu sur du JSON, facile et sans prise de tête. Mais le JSON est deux fois plus gros que le format binaire.
Par ailleurs, pour la revue de code chez Mozilla, c’est clair que le binaire, c’est un format obscur dont ils se méfient. C’est pourquoi je suis resté avec le JSON, qui semble moins inquiétant.

Pour le tableau de perf, c’est une dizaine de paragraphes différents analysés par le correcteur grammatical.

La suggestion est très gourmande, mais le gain est très appréciable avec le précalcul du binaire.

Pour la dernière remarque, oui.
le 12 septembre 2020 à 14:07
Je ne sais passe si tu as vu mais je t'ai envoyer un email pour éventuellement changé la partie suggestion qui d’ailleurs en modifiant juste dans la nouvelle fonction "jaro_winkler.distance" le 0.7 par 0.65 donne encore de meilleur résultat et pourrais permettre de se passé distanceDamerauLevenshtein ;)

Personnellement je trouve que dans la majorité des cas le gain de performance générale est pas mal sans que ça modifie trop les suggestions proposé.

C'est à toi de voir si c'est intéressant ou pas vu que cette partie est "délicate".
le 13 septembre 2020 à 13:13
Oui, j’ai vu, mais hier j’étais occupé.
le 14 septembre 2020 à 09:34
Pas de problème j'ai fait pas mal d'essaie hier et j'ai corrigé un peut la nouvelle fonction (elle a un petit problème) et simplifié le code. Et j'obtiens des performance meilleurs pour tous les cas que j'ai pu tester.
Je t'envoie ça dans pas longtemps si ça t’intéresse évidement ;)
le 14 septembre 2020 à 09:56
Oui, OK, je n’ai pas encore eu le temps de m’y consacrer. J’attends ton nouvel envoi.
le 14 septembre 2020 à 10:31
C'est envoyé !

Petite note, j'ai repris quelque uns de tes mots. Ça fonctionne bien aussi sans utilisé du tout la fonction simplifyWord sauf pour le cas qui est peut être un peu être un peu délirant : "déelirranttesss"
le 14 septembre 2020 à 11:10
Oh peut être trouvé pour le cas de "déelirranttesss"
En ajoutant dans str_transform:


simplifyWordOther: function (sWord) {
// word simplication other
return sWord.replace(/(.*)(.)(.\2)/,'$1$2').replace(/(.*)(\1\1)+/,'$1');
},


et donc dans ibdawg pour le calcul de distance dans addSugg

let nDistJaro = 1 - jaro_winkler_distance(this.sSimplifiedWord, str_transform.simplifyWordOther(sSugg))



ça permet de ne pas trop ralentir les autres mais améliores les suggestions.
le 14 septembre 2020 à 11:52
Qu’est-ce que tu reproches à simplifyWord ? Effectivement, elle ralentit parfois un peu d’un côté la suggestion, mais elle l’accélère énormément dans certains cas, et surtout permet de débroussailler la multitudes diacritiques.
Elle n’est pas toujours très utile, mais quand elle l’est, elle l’est vraiment.
Tu ne peux pas te baser uniquement sur les exemples du test, qui sont loin d’être représentatifs de tous les cas de figure.
Il est vrai qu’elle a un perdu de son intérêt dans certains cas, mais uniquement parce que j’ai introduit la distance entre caractères dans le calcul de distance des mots, qui fait en partie doublon.
le 14 septembre 2020 à 12:02
Dans le addSugg elle fait un peu trop à mon avis. Dans certain cas elle double de temps de recherche... Sans pour autant avoir vraiment de meilleurs résultat à mon gout.

Fix de la regex de la version 'light' ajouté


simplifyWordOther: function (sWord) {
// word simplication other
return sWord.replace(/(.*)(.)(.\2)/,'$1$2').replace(/(.)(\1)+/,'$1$1');
},



Je teste aussi d'autre mots qui me viennent en tête lol mais les testes de performance je les fait toujours avec les même.
Avec toutes les modifications proposé au pire la suggestion met autant de temps qu'avant, j'ai pas trouvé de cas ou c'est plus lent.
le 14 septembre 2020 à 12:27
Autant qu'on transforme le mot saisie par l'utilisateur pour "corrigé" des fautes évidentes et limité les cas c'est utile. Mais dans addSugg l'éventuel mots envoyer on sais qu'il est valide donc autant le modifié le moins possible vue que la fonction de distance fait déjà un travail de corrélation.

Sinon par exemple pour tous ce qui est


['œ', 'oe'], ['æ', 'ae'],
["⁰", "0"], ["¹", "1"], ["²", "2"], ["³", "3"], ["⁴", "4"], ["⁵", "5"], ["⁶", "6"], ["⁷", "7"], ["⁸", "8"], ["⁹", "9"],
["₀", "0"], ["₁", "1"], ["₂", "2"], ["₃", "3"], ["₄", "4"], ["₅", "5"], ["₆", "6"], ["₇", "7"], ["₈", "8"], ["₉", "9"]


je doute qu'un utilisateur qui ne veux pas saisir ses caractères le fasse... donc est-ce vraiment utile ? Je suis même sure que plus de 90% des utilisateurs ne savent même pas les faire.

Autre exemple les


['ſ', 's'], ['ffi', 'ffi'], ['ffl', 'ffl'], ['ff', 'ff'], ['ſt', 'ft'], ['fi', 'fi'], ['fl', 'fl'], ['st', 'st'],

ne sont pas présent dans le dico donc pourquoi essayer ces transformations dans addSugg.
le 14 septembre 2020 à 12:49
La vitesse ne peut pas être le seul critère, la justesse est un autre critère.

La fonction simplifyWord doit être la même pour les mots entrés par l’utilisateur et ceux dictionnaires…
Si vous ne comparez pas un mot simplifié avec une suggestion simplifiée, ça perd son intérêt, quand bien même ce n’est pas toujours nécessaire et que ça donne le même résultat dans de nombreux cas. Parce que vous ne pouvez pas savoir à l’avance si ce sera utile.
Soit vous simplifiez les deux mots (celui entré par l’utilisateur et celui suggéré), soit vous n’en simplifiez aucun. Sinon vous aurez de gros efforts de bord et des absurdités.

L’idée de cette fonction, c’est faire en sorte que la distance entre certains mots soit égale, en dépit de ce que disent les calculs usuels des fonctions de calcul de distance.

Exemples :

trèèèèèèèès = très
vraaaaaiiiiiimeeeennt = vraiment
apele = appelle
émpâller = empaler
CO2 = CO₂


Sans cela un simple calcul de distance est vite perdu dans les possibilités.
le 14 septembre 2020 à 13:18
Avec l'implémentation de ibdawg que je vous ai envoyé même si on applique simplifyWord nous avons un gain mais un peu réduit (4840 ms au lieu de d'un gain de 5758ms (temps de ref 11734ms)) ce qui reste plus rapide avec des suggestions plus ou moins similaire.
le 14 septembre 2020 à 14:05
Mes tests :

Damerau-Levenshtein
fatiqué -> phatique fatigue fatigué favique attique phatiques photique -- 244.07 ms
coeur -> sœur cœur sieur sueur sœurs sauteur sauveur codeur cour chœur -- 77.95 ms
trèèèèèèèèès -> très erres stress ires ares terres terrés tires tirés tares -- 120.22 ms
vraaaaiiiimeeeeennnt -> vraiment trainement traînement braiment friment -- 715.28 ms
apele -> appelé appelle atèle appeler appelez appels appela appelai appelée -- 35.16 ms
email -> courriel e-mail émail -- 0.21 ms
Co2 -> CO₂ CO2 SO₂ Co ² Soi Son Sot Sol Sou Soc -- 46.29 ms
emmppâiiiller -> empailler empaillée empailles empaillera empaillez empaille empaillés empaillé empailleur -- 25.14 ms
testt -> test teste tests testa testé tes tech teck tec -- 24.17 ms
apelaion -> appelaient appelions appellation appellerons appelleront appellerions pelain spéléo spéléos appellations -- 176.72 ms
exsepttion -> exception exceptions -- 12.68 ms
sintaxik -> sirtaki sirtakis cintrais cintrait cintrai -- 90.22 ms
ebriete -> ébriété abrite abrité écrite ébriétés ébruite débriefe Henriette brayette -- 54.8 ms
ennormmement -> énormément encordement endormaient engorgement enfournement -- 138.75 ms

Jaro-Winkler
fatiqué -> fatigue fatigué fatiguée fatigués fatiguées fatiguai fatiguais fatiguait attique -- 138.24 ms
coeur -> sœur cœnure cœnures cœur cœurs chouré sieur soyer soufrai -- 96.37 ms
trèèèèèèèèès -> très Dres ardé tersées tersée terses terse terser tersez -- 184.76 ms
vraaaaiiiimeeeeennnt -> vraiment traitement braiment braiments fragmente fragmenté fragmentée -- 260.48 ms
apele -> appeler appelez appels appela appelai appelé appelée appelés appel -- 35.21 ms
email -> courriel e-mail émail -- 0.25 ms
Co2 -> Co₂B CO₂ CO2 Cos Coi Cor Con Col Coll Cou -- 47.98 ms
emmppâiiiller -> empaillée empaillées empailles empailler empaillerez empaillera empaillez empaille empaillés empaillé -- 26.17 ms
testt -> teste testes tester testez tests testa testas testai testé testée -- 29.03 ms
apelaion -> appelaient appelions appellerons appelleront appellation appellations appellerions appellent -- 38.73 ms
exsepttion -> exception exceptions exceptons exaspérions acceptions -- 11.85 ms
sintaxik -> sirtaki sirtakis -- 23.16 ms
ebriete -> emboite emboites emboiter emboitez abrites abriter abritez abritée abritées abrité -- 34.46 ms
ennormmement -> encombrement encombrements énormément -- 46.92 ms

C’est parfois plus rapide, parfois moins, mais c’est aussi moins pertinent.
le 14 septembre 2020 à 15:25
Vous vous êtes basé sur quel version ? car c'est bizzard voici les résultats que j'obtiens

fatiqué (10) ["fatigué", "fatiguée", "fatigués", "fatigue", "attique", "fatiguées", "fatigues", "fatiguer", "fatiguez", "favique"] 108.63623046875 ms
coeur (10) ["courre", "courra", "courrez", "courras", "courrai", "chouré", "coter", "coder", "coyer", "coxer"] 37.48828125 ms
trèèèèèèèèès (10) ["très", "terrer", "terrerez", "terrera", "terrier", "terrez", "tercer", "terre", "terré", "tirer"] 149.761962890625 ms
vraaaaiiiimeeeeennnt (10) ["vraiment", "fragmentai", "tramant", "cramant", "brahman", "bramant", "fragmenta", "erraient", "fragmentas", "braiment"] 65.0009765625 ms
apele (10) ["pelle", "pelles", "peller", "pellet", "pellez", "pellé", "pellera", "pellets", "pelleta", "pelleté"] 35.1669921875 ms
email (10) ["émail", "small", "mails", "empile", "empila", "empilé", "empale", "empala", "empalé", "émaille"] 33.467041015625 ms
Co2 (10) ["CO2", "CM2", "CE2", "CoS₂", "Co₂B", "CoF₂", "CoI₂", "Cook", "Corse", "CoCl₂"] 19.196044921875 ms
emmppâiiiller (10) ["empailliez", "empaillées", "rempaillerez", "empaillera", "empailleur", "empaillerez", "empailleurs", "rempaillée", "rempaillés", "rempaillées"] 10.2119140625 ms
empaillé (10) ["empaillé", "empaillée", "empaillés", "empaille", "empaillées", "empailles", "empailler", "empaillez", "empailla", "empailliez"] 22.538330078125 ms
testt (10) ["test", "teste", "tests", "testa", "testé", "tes", "tette", "testes", "tester", "testez"] 9.864990234375 ms
apelaion (10) ["palliions", "pellions", "appelions", "pellaient", "épelions", "pelaient", "pallions", "pelain", "pelains", "pelions"] 27.136962890625 ms
appellerions (10) ["appellerions", "appellerons", "appelleront", "capellerions", "appelle irons", "tavellerions", "gamellerions", "batellerions", "babellerions", "javellerions"] 34.80615234375 ms
exsepttion (7) ["exceptions", "exception", "exaspérions", "exceptons", "acceptions", "acception", "encrêpions"] 7.869140625 ms
sintaxik (2) ["sirtaki", "sirtakis"] 11.453857421875 ms
ebriete (10) ["abritée", "ébruite", "briefée", "ébriété", "briefe", "abritées", "ébruites", "ébruiter", "ébruitez", "ébriétés"] 10.916015625 ms
ennormmement (3) ["encombrement", "encombrements", "énormément"] 12.58203125 ms
le 14 septembre 2020 à 15:46
Si tu as pris une version toutes faite, faut faire attention car c'est une version qui prend en compte un "ajustement" que j'ai utilisé dans le fichier que je t'ai envoyé. (il y a le lien de la source et dans la source d'ou je l'est prise il y a le lien version la version qui l'a inspiré qui est en C).

Si j'avais eut des résultats comme les tiens je t'en aurais pas parlé lol
le 14 septembre 2020 à 15:59
J’ai corrigé le message précédent, parce que j’avais oublié que le résultat de Jaro-Winkler était très différent en nature de celui de Damerau-Levenshtein.

C’est bien mieux, mais pas toujours plus rapide, pas toujours plus pertinent.
J’utilise la fonction simplifyWord originale.

Il est important d’éviter de modifier plusieurs fonctions à la fois pour comparer les méthodes. C’est pourquoi je n’ai changé que la fonction de comparaison. En fait, ton benchmark est altéré parce que tu as modifié aussi simplifyWord.
C’est plus cette fonction que Damerau-Levenshtein qui ralentit la tâche.

Je tourne sur un AMD Ryzen 2700X. Quel est ton processeur ?
le 14 septembre 2020 à 16:29
Un vieux (de 2012) i5-2500k 3.30Ghz avec 16Go de mémoire.

Dans mes tests au pire j'obtiens un temps a peu près similaire.

Pour le coup de "coeur" j'ai compris pourquoi dans ma version ça propose pas cœur. J'ai été un peu trop a l'arrache vu je n'avais pas voulu trop changé le code. Même si dans la dernière version envoyer j'avais fait un petit ménage lol

Oui les résultat entre les deux Jaro-Winkler et Damerau-Levenshtein sont très différent et la bidouille utilisé fait que la Jaro-Winkler deviens beaucoup moins précise par rapport a ce qu'elle est normalement. Je te fais une version plus adapté pour Jaro-Winkler en mettant ma petite fainéantise de coté lol J'ai une idée sur comment faire en limitant la perte de performance.
le 14 septembre 2020 à 16:45
En ajoutant quelques cours-circuit (adapté a Jaro) et adaptation (j'ai pas modifié le oDistanceBetweenChars qui mériterais peut être d'être un peu adapté).

Voici les derniers résultats les chiffres dans {} sont ceux avec Damerau-Levenshtein (tronqués) a part pour Co2 les autres me semble plutôt pertinent :

fatiqué (9) [phatique, phatiques, fatiguai, fatigue, attique, fatiguait, fatiguez, favique, fatigua] 140.98388671875 ms {147}
coeur (10) [cœur, cœurs, sœur, cœnure, cœnures, couru, sœurs, colure, couard, coutre] 14.208984375 ms {27}
trèèèèèèèèès (5) [très, terrer, terrerez, terrera, terrez] 39.078857421875 ms {36}
vraaaaiiiimeeeeennnt (10) [vraiment, fragmentai, fragmenta, erraient, fragmentas, braiment, virement, fragmenter, fragmenté, fragment] 77.280029296875 ms {214}
apele (10) [appelez, appel, appelés, appela, appelait, applet, appelât, asple, ample, asples] 2.843994140625 ms {14}
email (8) [émail, empile, Emily, mails, empila, empilé, émaille, émailla] 45.1806640625 ms {77}
Co2 (10) [CO2, Co₂B, CM2, CE2, CoS₂, CoF₂, CoI₂, Cloé, Ko, Zo] 7.661865234375 ms {16}
emmppâiiiller (9) [empailler, empaillera, empaille, empaillerez, empaillez, empailliez, empaillés, empailleurs, émaillerez] 8.335205078125 ms {9}
empaillé (9) [empaillés, empaille, empaillées, empaillez, empailla, empaillent, empaillas, empaillerez, espériez] 0.842041015625 ms {21}
testt (8) [test, tes, testât, testâtes, teseta, teseté, tesète, tes th] 2.423828125 ms {9}
apelaion (8) [appelions, appelaient, appelleront, appellerions, appellation, appellations, appellent, spéléos] 36.008056640625 ms {121}
appellerions (6) [appellerions, appelleront, appèterions, appelle irons, appelle rions, arpégerons] 8.902099609375 ms {15}
exsepttion (7) [exception, exceptions, exceptons, exaspérions, acceptions, acception, encrêpions] 3.403076171875 ms {7}
sintaxik (2) [sirtaki, sirtakis] 6.828857421875 ms {28}
ebriete (10) [abritée, ébruite, briefée, ébriété, briefe, emboite, abritées, ébruites, ébruiter, ébruitez] 11.99365234375 ms {19}
ennormmement (3) [encombrement, énormément, encombrements] 13.324951171875 ms {46}
Total: 420.856201171875 ms {816}

PS: les suggestion reste meilleur que celle faite la le correcteur de chrome mdr
le 14 septembre 2020 à 19:09
J'ai envoyé un email avec des modification et donc maintenant si certains autre suivent j'obtiens :

fatiqué : 16.644999850541353 => fatigué, phatique, fatiguée, fatigués, fatiguai, phatiques, fatiguées, fatiguais, fatiguait
coeur : 4.670000169426203 => cœur, cœurs, sœur, chouré, cousu, coure, cours, court, couru
trèèèèèèèèès : 3.4050000831484795 => très, trié, tris, trière
vraaaaiiiimeeeeennnt : 23.520000278949738 => vraiment, braiments, braiment, erraient, iraient, gréements, gréement, trémens, crèment
apele : 1.5799999237060547 => appeler, appelez, appelé, appel, appelai, appelée, appelés, appels, appela
email : 4.110000096261501 => émail, emmêle, emmêla, emmêlé, empile, empila, empilé, émaille
Co2 : 13.110000174492598 => CO₂, CO2, Co₂B, CM2, CoS₂, CoF₂, CoI₂, Cloé, Cos, Coi
emmppâiiiller : 1.4400002546608448 => empailler, empaillera, empaille, empaillerez, empailles, empaillez, empaillée, empaillé, empilera
empaillé : 0.9550000540912151 => empaillé, empaillai, empaillée, empaillés, empaillées, empaille, empailla, empailles, empailler
testt : 1.9049998372793198 => test, teste, tests, testa, testé, tes, testes, tester, testez
apelaion : 12.455000076442957 => appelions, appelaient, appellerions, appellerons, appelleront, appellation, appellations, appellent, spéléos
appellerions : 7.679999805986881 => appellerions, appellerons, appelleront, appéterions, appèterions, appelle irons, appelle rions
exsepttion : 11.5450001321733 => exception, exceptions, exceptons, exaspérions, acceptions, acception, encrêpions
sintaxik : 11.414999607950449 => sirtakis, sentais, syntaxique, syntaxiques, syntacticien, syntactique, syntacticiens, syntactiques
ebriete : 5.859999917447567 => abritée, ébriété, abritées, ébriétés, abrites, abriter, abritez, abrité, abritera
ennormmement : 17.84499967470765 => encombrement, énormément, encombrements
Total: 142.27490234375 ms

Je pense qu'on a donc la performance et la pertinence maintenant
le 15 septembre 2020 à 11:06
OK, j’ai regardé ton envoi ce matin.
La logique est un peu tortueuse (mais pas vraiment plus que ce que je faisais précédemment), les résultats sont probants et ça va plus vite, voire vraiment plus vite, malgré quelques exceptions sans importance.

C’est donc acceptable.

Quelques remarques :
— coefPreMaj n’est pas utile, attendu qu’on coupe le nombre de suggestions après le tri des suggestions et non avant.
— Il ne faut pas utiliser le drapeau \b dans les regex, car seul l’ASCII est reconnu. Par exemple, avec ik\b, la regex matche avec un mot comme “aiké” (au lieu de “ailé”) pour en faire “aiqueé”.
— Cela a-t-il un intérêt de faire un match() avant de lancer les replace() ?
— Dans Jaro-Winkler, pourquoi avoir ôté la ligne suivante ?

if (a == b) { return 1.0; }



Edit: je réponds au message #43. Je n’ai pas encore regardé l’envoi du message #44.
le 15 septembre 2020 à 11:26
Le if (a == b) { return 1.0; } était en commentaire... donc vu que j'ai fait du ménage j'ai viré le code qui était en commentaire.
Le coefPreMaj permet que s'il faut faire la conversion en majuscule la conversion se fait sur une liste plus petite ça sert a rien qu'elle se fasse sur une énorme liste. Et vu qu'il y a une marge même si la mise en majuscule fait que certains résultat ne deviennent plus qu'un on dois avoir au final le bon nombre.

D'accord pour les regexs ça ma toujours bien pris la tête lol en plus en fonction du langage de programmation il y a des différences :'( il faut donc remplacé le \b par $ c'est bien ça ? pour le match vu que la nouvelle fonction est utilisé qu'une fois effectivement c'est peut être pas nécessaire.

Avec le #44 je crois qu'il n'y a plus aucun cas ou c'est plus lent / équivalent!

[EDITED]
le 15 septembre 2020 à 12:31

Le if (a == b) { return 1.0; } était en commentaire... donc vu que j'ai fait du ménage j'ai viré le code qui était en commentaire.


En fait, je me demandais aussi pourquoi c’était mis en commentaire.

Le coefPreMaj permet que s'il faut faire la conversion en majuscule la conversion se fait sur une liste plus petite ça sert a rien qu'elle se fasse sur une énorme liste. Et vu qu'il y a une marge même si la mise en majuscule fait que certains résultat ne deviennent plus qu'un on dois avoir au final le bon nombre.


Oui, en effet, j’ai parlé un peu vite. Cela dit, multiplier par 1,5, c’est bien trop. Je vais réduire ce nombre.

Oui, il faut remplacer \b par $.

pour le match vu que la nouvelle fonction est utilisé qu'une fois effectivement c'est peut être pas nécessaire.


Ça en aurait eu un, si c’était utilisé plusieurs fois ?
le 15 septembre 2020 à 13:25
Il me semble que j'avais fais un benchmark et que le replace quand il faisait rien demandais plus de temps que le match (sans doute a cause des optimisations du moteur javascript qui doivent garder les regex en cache). A vérifié...

Le 1.5 est surement un peu trop large c'est pour ça que j'ai mis en "paramètre" qui permet de pouvoir ajusté si c'est trop ou pas assez sans devoir le remplacé la valeur plusieurs fois lol

Comme fautes très courante qui peuvent être soulagé pour la nouvelle fonction cleanWord il y a :
* l'oubli du x pour les mots terminant pat eux => sWord = sWord.replace(/(.*)eu$/igm,'$1eux');
* et le np dans un mot à la place de mp => sWord = sWord.replace(/(.*)np/igm,'$1mp');
Après là c'est plus de la micro-optimisation... vu que de toutes façon c'est cas sont quand même retrouvé contrairement au coup du ik ;)
le 15 septembre 2020 à 13:46
La micro-optimisation, plus tard en effet.

Y a-t-il une raison pour tu écrives :


if (nJaroA == nJaroB) {
return (b[1] > a[1]) ? -1 : (b[1] < a[1]) ? 1 : 0;
} else {
return (nJaroB < nJaroA) ? -1 : 1;
}



au lieu de :

if (nJaroA == nJaroB) {
return a[1] - b[1];
} else {
return nJaroB - nJaroB;
}



C’est bien plus lisible (mais je ne sais pas si c’est plus rapide), et ça permet plus facilement de voir que tes tris ne sont pas identiques.

Dans le premier cas, tu renvoies 1 si a > b.
Dans le second cas, tu renvoies 1 si b > a.

(Ce n’est pas une erreur dans ton algo, puisque les deux listes ne sont pas triées pareillement, mais dans le genre confus, c’est pas mal, parce que plus tard, je doute qu’on se souvienne de cette subtilité.)
le 15 septembre 2020 à 14:28
Effectivement ! Niveau rapidité ça reviens au même ;)
Comme quoi c'est toujours bien quand quelqu'un lit le code d'un autre...

Je ne sais pas si tu as vu mais j'avais glisser une petite note en commentaire dans la fonction "suggest"

//! Note: pourquoi à ce niveau ? on aurait pu surement avoir une valeur supplémentaire autre.
if (this.lexicographer) {
aSugg = this.lexicographer.filterSugg(aSugg);
}


donc si ont regarde le code de cette fonction


filterSugg: function (aSugg) {
return aSugg.filter((sSugg) => { return !sSugg.endsWith("è") && !sSugg.endsWith("È"); });
}


donc quoi qu'il arrive actuellement on ne peut pas avoir de réponse terminant par "è" pourquoi ?
le 15 septembre 2020 à 15:16
Les graphies finissant par “è” n’existent que dans certaines formes verbales interrogatives dans la réforme de 1990, par exemple “restè-je”. C’est très rare et ce n’est valable que dans cette forme-là. Les suggérer revenait à rendre assez confus certaines suggestions de participes passés comme “resté”.

Bon, je vais maintenant convertir tout ça en Python.
le 15 septembre 2020 à 15:25
Merci pour l'explication!
Bon courage !

Vu toutes les optimisations ça méritera bien que Grammalecte passe en version 2 ;)
le 15 septembre 2020 à 15:36
Voilà, c’est fait.
Merci d’avoir fait des benchmarks sur tout ça. La suggestion est vraiment plus rapide.
le 15 septembre 2020 à 19:18
Ça profite a tous donc autant aider quand on peut ;)
Juste par curiosité est-ce le même type de gain sur python ?
le 15 septembre 2020 à 19:24
Oui. Même un peu plus, je pense.
le 15 septembre 2020 à 20:10
D'accord ! Je pense qu'on peux fermer le sujet performance de graphspell maintenant (je pense que se qui reste est plus de la micro-optimisation et donc peut être fait plus tard..) !
Et merci a toi de ta patience et de tout le travail que tu fais sur Grammalecte depuis quelques années déjà.
le 15 septembre 2020 à 20:35

Notification par e-mail    1