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 Perl Discussion :

améliorer performance script


Sujet :

Langage Perl

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France, Morbihan (Bretagne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 45
    Points : 29
    Points
    29
    Par défaut améliorer performance script
    Bonjour,

    comment augmenter la rapidité d'exécution d'un script Perl ?

    Je ne cherche pas des moyens d'optimiser ma manière d'écrire (par exemple ça : http://www.destailleur.fr/mes-activi...erformances-de).

    Mon but : ayant un code déjà écrit, existe-t-il des moyens pour accélérer son exécution (compilation, parallélisation, etc...) ?

    merci d'avance.

  2. #2
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Outre les styles d'écriture de code, il y a très souvent des améliorations notables à chercher du côté de l'algorithmique (notamment décomposition en opérations "parallélisables", mais aussi analyse de la complexité des algorithmes)).
    Mais pour cela, il faut avoir quelques détails sur ton script et son algorithme.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  3. #3
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Il est pratiquement possible de t'aider concrètement sans voir le code.

    Mais autant te prévenir tout de suite: les vraies améliorations de performances, celles qui qui sont susceptible de diviser les tremps d'exécution par des facteurs de 2, 5, 10 ou même plus, sont celles qui modifient au moins en partie l'algorithme, utilisent des fonctions plus adaptées, mettent en cache les données utilisées plusieurs fois, et ainsi de suite.

    Sinon, tu peux toujours consulter la vidéo d'une présentation que j'avais faite aux Journées Perl 2013 sur l'amélioration des performances en Perl:

    http://djibril.developpez.com/tutori...perl/2013/#LXV

  4. #4
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France, Morbihan (Bretagne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 45
    Points : 29
    Points
    29
    Par défaut
    merci de ces réponses.

    Ma question était au sens large (i.e pas dédié à un script précisément). En gros, une solution miraculeuse et générale (par exemple : des options de compilation pour en faire un binaire).
    Tant pis.

    Ceci dit. Je serais curieux d'avoir un avis sur comment améliorer en écrivant mieux le code. Je peux fournir un exemple de script mais ça va peut-être vous demander du temps (rien que le temps pour arrêter de pleurer en voyant comment j'ai codé ça). Si ça vous intéresse, je peux faire une archive à télécharger : script + fichiers nécessaires pour faire tourner le traitement sur un exemple concret.
    J'ai conscience du temps que ça va demander pour comprendre le contexte du script, le code en lui-même, envisager une meilleure écriture, etc...

  5. #5
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Tente toujours, si jamais quelqu'un a du temps, tu n'auras pas perdu grand chose.

    Les solutions de compilation peuvent sans doute apporter quelques améliorations, cependant, je les vois plus comme "permettant d'obtenir un exécutable indépendant de la plateforme perl", c'est à dire autonome et déployable sur un parc d'ordinateur sans nécessiter l'installation de perl.
    La compilation peut aussi engendrer des limitations. Il y a un tuto sur différentes méthodes de compilation (ce n'est pas vraiment une compilation, on devrait plutôt parler d'empackatage ; d'ailleurs, l'interpréteur Perl précompile le code source, de sorte qu'il n'est plus interprété dynamiquement, sauf exception, ce qui le rend particulièrement rapide par rapport à d'autres langages de script).
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  6. #6
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Yep, montre ce que tu as, on aura peut-être des idées.

  7. #7
    Responsable Perl et Outils

    Avatar de djibril
    Homme Profil pro
    Inscrit en
    Avril 2004
    Messages
    19 820
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 19 820
    Points : 499 184
    Points
    499 184
    Par défaut
    Bonjour,

    Je vais juste répéter les commentaires de mes amis ci-dessus, pour optimiser un programme, voici les solutions :
    1. avoir un algorithme solide (quelque soit le langage, c'est la solution n°1) ;
    2. paralléliser si le programme, l'algorithm le permet (encore faut-il que la machine le permette en ayant plusieurs processeurs) ;
    3. avoir un matériel, des disques puissants si le programme doit faire des accès disques importants.

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France, Morbihan (Bretagne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 45
    Points : 29
    Points
    29
    Par défaut
    bon, j'ai préparé un exemple de script. Voici le lien pour télécharger l'exemple :
    https://mon-partage.fr/f/bNU33doR/

    Il s'agit d'une archive (100 Mo environ) compressée (commande unix : tar -zcvf ....) qui contient le script (exemple_script.pl) et les 3 fichiers nécessaires pour faire tourner un exemple d'utilisation. J'ai également créé un readme (1README.txt) avec des infos en plus de celles déjà fournies en commentaires du script
    une fois décompressé, le tout devrait faire environ 250 Mo (il y a un gros fichier ASCII contenant des données à traiter)

    dans le readme, j'ai essayé d'expliquer le contexte. Je ne cite que les grandes étapes du script. Ces étapes sont un peu plus détaillées en commentaires dans le script. Mais si il y a le moindre problème de compréhension, je rajouterai ce qu'il faut là où il y a besoin.
    Pour lancer ce script, j'utilise un terminal unix. Dans le readme, vous trouverez la ligne de commande permettant de faire tourner l'exemple (en tout cas, ça vous donne l'ordre dans lequel les arguments doivent être donnés au cas où vous l'exécuteriez dans un autre environnement, genre Windows).
    J'ai mis un exemple de temps d'exécution en fin du fichier 1README.txt (en précisant les carac de ma machine). Donc, environ 8 minutes sur ma machine. Plus il y a de choses à faire dans l'ETAPE 4, plus le temps d'exécution explose (j'ai d'autres exemples où ça dure plusieurs heures).

    dans les commentaires, je mentionne parfois le package perl que j'aurais sans doute dû utiliser au lieu de coder des lignes inutiles. Mais à chaque fois, cela n'aurait pas éliminé le problème de temps d'exécution. Car le problème, c'est l'ETAPE 4

    merci infiniment d'avance. J'ai bien hate d'avoir un retour. J'ai l'impression de passer un examen

  9. #9
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    en fait, la première chose à faire serait de profiler ton code pour déterminer précisément où il passe beaucoup de temps. L'outil de choix serait: http://search.cpan.org/~timb/Devel-N...vel/NYTProf.pm.

    Je n'ai pas le temps maintenant d'essayer.

    Cela dit, quelques remarques rapides après un bref survol du code et un petit test sous le débogueur.

    Dans le bout de code (abrégé) suivant:
    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
     
    for(my $noeud_1=1; $noeud_1<=$nb_noeuds_1; $noeud_1++) {
      $noeud_2 = $tab_corresp_noeud_1_2[$noeud_1];
     
    # ...
     
      my $no_incr = 0;
      open(FIC, "<$fgmsh_dpl");
      while(<FIC>) {
        next if(not /^\s*\$NodeData\s*$/);
        $no_incr++;
        $_ = <FIC>; $_ = <FIC>; $_ = <FIC>; $_ = <FIC>;
        /($format_reel)/;
        $temps = $1;
        $_[0] = <FIC>; chomp; $_[1] = <FIC> for(1 .. $_[0]);
        @table_dpl_1 = ();
        while(<FIC>) {
          last if(/^\s*\$EndNodeData\s*$/);
          next if(not /^\s*(\d+)\s+($format_reel)\s+($format_reel)\s+($format_reel)\s*$/);
          ($noeud_lu, $UX, $UY, $UZ) = ($1,$2,$3,$4);
          next if(not $noeud_lu == $noeud_1);
          push(@liste_temps, $temps);
          push(@liste_dpl_UX, $UX);
          push(@liste_dpl_UY, $UY);
          push(@liste_dpl_UZ, $UZ);
          last;
        }
        while(<FIC>) {last if(/^\s*\$EndNodeData\s*$/);}
      }
      close(FIC);
     
    # ...
     
      close(F_UY);
      close(F_UZ);
    }
    tu lis apparemment ton fichier de 5 millions de lignes de très nombreuses fois (plusieurs centaines de fois, peut-être ?). Pas étonnant que ça prenne du temps. Je remarque aussi que tu sembles écarter pas mal de lignes à chaque fois.

    N'est-il pas possible de charger ces données en mémoire en ne gardant que les données utiles? Ou, si ce n'est pas possible (trop gros pour la mémoire, par exemple), de lire une seule fois le gros fichier et d'en faire une copie temporaire avec les seules lignes utiles, afin de le lire ensuite de nombreuses fois qu'un fichier plus petit, expurgé des lignes inutiles.

    Je ne vois pas l'intérêt de créer des milliers (dizaines de milliers?) de tout petits fichiers temporaires s'il s'agit de les concaténer par la suite. N'est-il pas possible d'écrire directement dans le ou les fichier(s) de résultat? Cela peut faire une différence de durée significative (les ouvertures et fermetures de fichiers sont assez coûteuses quand on le fait des milliers ou des dizaines de milliers de fois, comme ça semble être le cas). Surtout s'il faut recommencer et relire tous ces petits fichiers pour les concaténer.

    Selon la version de Perl que tu utilises, il se peut que ce simple changement de définition de ta regex $format_reel fasse une différence significative:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    my $format_reel = qr/[+-]?[\.]?\d+[\.]?\d*(?:[eE][+-]?\d*)?/;
    Ou peut-être remplacer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
          next if(not /^\s*(\d+)\s+($format_reel)\s+($format_reel)\s+($format_reel)\s*$/);
          ($noeud_lu, $UX, $UY, $UZ) = ($1,$2,$3,$4);
    par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
          ($noeud_lu, $UX, $UY, $UZ) = split /\s+/, $_;
    Je soupçonne que les deux lignes ci-dessus prennent peut-être beaucoup de temps, mais, encore une fois, seul un profilage de code pourra le dire avec certitude.

    Voilà, quelques conseils rapides, tu peux essayer de les mettre en œuvre, mais le profilage du code est vraiment la première chose à faire.

    J'essaierais peut-être de revenir plus tard si j'ai la possibilité mais ce n'est pas gagné, j'ai une réunion ce soir.

    Bonne soirée,
    Laurent.

  10. #10
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Personnellement, je n'aurais sans doute pas le temps de regarder avant lundi.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France, Morbihan (Bretagne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 45
    Points : 29
    Points
    29
    Par défaut
    merci de ces retours.

    Je n’ai pas pu profiler mon code avec «*use Devel::NYTProf*». Ce package n’est pas dispo sur ma machine. Je verrai plus tard pour l’installer.

    En attendant, j’ai travaillé sur mon code pour essayer de l’améliorer.

    1) j’ai qr// ma regex pour la recherche (\d+)\s+($format_reel) etc… (ce qui inclut un qr de $format_reel). Je n’ai pas noté le temps d’exécution. Le gain était visible mais minime (vingtaine de secondes). A terme, il faudra effectivement remplacer ça par un split (surement moins couteux qu'une regex, merci de ce conseil)

    2) concernant la lecture du gros fichier de 5 millions de lignes et l’écriture de fichiers temporaires :
    Je préfère ne pas stocker en mémoire ces données par sécurité. Un jour, le script aura peut-être à faire avec un très très gros fichier de données.
    Concernant le pourquoi j’ai choisi d’écrire plein de fichiers temporaires, je sèche un peu. Il me semble que quand j’avais fait la première version de ce script, j’écrivais directement dans le fichier final. Mais je ne sais plus pourquoi j’ai finalement fait le choix de la séparation. A ma défense, je dirai que l’opération de concaténation prend vraiment peu de temps.

    D’ailleurs, cette séparation de fichiers m’a permis de tester à l’arrache une parallélisation via «*use threads*». (nouveau code en pièce jointe)
    C'est la première fois que j'utilise les threads. J'ai donc bidouillé. J’obtiens ce temps pour 3 threads en parallèle (ma machine à 4 processeurs):
    607.829u 10.044s 4:14.85 242.4% 0+0k 0+230io 0pf+0w
    c’est-à-dire environ 4 minutes au lieu de 8 => facteur 2
    (edit : j'aurais bien voulu essayer ça : "use Parallel::ForkManager" mais pas dispo sur ma machine actuellement)

    pas mal, mais un facteur 2, c’est pas transcendant.
    En fait, chaque traitement de l’ETAPE 4 prend un temps de l’ordre de grandeur de l’ETAPE 3. Donc, évidemment, ça va durer nb_traitement fois plusieurs secondes = des heures quand il y a des milliers de traitements à faire.

    Il faut donc réduire ce temps de traitement :
    1- comme dit précédemment, je ne veux pas stocker en mémoire car un jour je le regretterai
    2- l’idée de réécrire un fichier plus petit est bonne mais dans mon cas, ça ne va pas être efficace car la plupart des lignes contiennent des données utiles. Par exemple, imaginons que je veux traiter les 3362 noeuds de l’exemple (au lieu de 86), la réécriture de fdpl_1.pos conduirait à obtenir 5545201 lignes au lieu de 5551877 lignes, soit un gain dérisoire. Cette solution n’aura donc que peu d’influence pour les gros traitements (ceux qui durent des heures).
    MAIS, peut-être une solution serait de mémoriser des numéros de lignes «*clé*» et/ou des compteurs d’intervalle pour accélérer le parcours de ce fichier. C’est-à-dire remplacer des «*while(<>) {next if(/regex/}*» par des «*for(bla bla) {<>}*»* pour me positionner directement sur les bonnes lignes. Ce pré-traitement peu être fait dans l’ETAPE 3.

    je suis là-dessus actuellement, je vous dis le résultat dès que possible.
    Fichiers attachés Fichiers attachés
    • Type de fichier : pl exe.pl (34,2 Ko, 46 affichages)

  12. #12
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France, Morbihan (Bretagne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 45
    Points : 29
    Points
    29
    Par défaut
    modif en enregistrant au prealable dans l’ETAPE 3, des numéros de ligne de départ (@LIGNE_TEMPS) et pour chaque noeud à traiter, le nombre de lignes supplémentaires à lire pour atteindre la bonne ligne (@INCR_LIGNE_TEMPS_POUR_NOEUD)
    résultat (toujours avec 3 threads) :
    327.427u 9.020s 2:35.33 216.5% 0+0k 0+133io 0pf+0w
    soit environ 2 mn 30 => nice!
    (nouveau code en pièce jointe)

    ps : comme conseillé, j’ai également remplacé /(\d+)\s+($format_reel)/ etc… par un «*split*» (seulement lorsqu’on est sûr d’être sur la bonne ligne)
    Fichiers attachés Fichiers attachés
    • Type de fichier : pl exe.pl (35,1 Ko, 42 affichages)

  13. #13
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Les performances dépendent trop du contenu de tes données en entrée, il est pratiquement impossible de déterminer manuellement ce qui prends vraiment beaucoup de temps. Le profilage du code est l'étape incontournable avant de faire quoi que ce soit.

    Le multithreading ou multiprocessing, oui, bien sûr, mais tu ne gagneras jamais qu'un facteurlimité au maximum nombre de cores ou de CPU disponibles (et encore, à condition que la machine n'ait que cela à faire).

    Enfin, quand tu fais de modifs pour tester le résultat d'une modification en termes de performances, il faut toujours faire une seule modif à la fois, sinon, une amélioration d'un côté peut masquer une dégradation de l'autre, ou l'inverse.

  14. #14
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France, Morbihan (Bretagne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 45
    Points : 29
    Points
    29
    Par défaut
    J'ai tenté un profilage avec Devel::NYTprof. Malheureusement, l'exécution me renvoie "Segmentation fault".

    Je lance :
    soit => perl -d:NYTProf mon_script.pl
    soit je le met dans l'en-tête => #!/usr/bin/perl -d:NYTProf

    je ne sais pas trop comment résoudre ce problème. Et d'ailleur, quel style de résultat cela devrait me renvoyer si ça marchait ?

  15. #15
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Résultat de profiler sur ton script : http://dl.free.fr/foBbm2Zwj (disponible 30 jours).
    Ouvrir le fichier index.html à partir d'un navigateur.
    L'exportation des résultats est également disponible au format CSV.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  16. #16
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Citation Envoyé par seagull Voir le message
    2) concernant la lecture du gros fichier de 5 millions de lignes et l’écriture de fichiers temporaires :
    Je préfère ne pas stocker en mémoire ces données par sécurité. Un jour, le script aura peut-être à faire avec un très très gros fichier de données.
    Je ne sais pas si l'argument tient : si l'on parle bien du gros fichier $fgmsh_dpl, il est lu une première pour remplir le tableau @table_dpl_1, puis plusieurs fois pour renseigner des tableaux @liste_dpl_UX, @liste_dpl_UY et @liste_dpl_UZ, mais grosso modo, même si les tableaux @liste_dpl_U* ne contiennent que des sous-ensemble du tableau @table_dpl_1, ils semblent contenir les mêmes données.

    Or, comme tu lis et tu mets en mémoire au moins une fois dans ton script ce fichier $fgmsh_dpl, je me demande bien pour quelle raison tu ne pourrais pas le conserver en mémoire le reste du temps de l'exécution (de toute manière, quel que soit la taille de ton fichier, il faudra que tu le charges entièrement au moins une fois à l'étape 3).
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  17. #17
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Un détail : appeler system("rm ...") à la place de l'utilisation de la fonction unlink n'est pas très optimal.
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  18. #18
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Le profilage de code dit que les lignes prenant le plus de temps sont:
    - Ligne 379: while(<FIC>) {last if(/^\s*\$EndNodeData\s*$/);}
    - Ligne 370: next if(not /^\s*(\d+)\s+($format_reel)\s+($format_reel)\s+($format_reel)\s*$/);
    - Lignes 368, 369

    La précompilation de la regex de la ligne 370 et des micro-optimisations (par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     while(<FIC>) {last if (index $_, '$EndNodeData') >= 0;}
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    last if  index ($_, '$EndNodeData') >= 0;
    m'ont permis de réduire d'un peu plus d'un tiers la durée d'exécution (de 9 min et quelques à 6 min), mais il est peu probable de pouvoir aller beaucoup plus loin dans cette voie.

    La vraie question à se poser: est-il possible de faire en sorte de ne pas avoir besoin d'exécuter 416.962.788 fois la ligne 379 (qui ne fait somme toute pas grand chose), 58.652.988 fois les lignes 369, 370, 371 et 372? A noter d'ailleurs que le last de la ligne 369 et le next de la ligne 370 ne servent en gros à rien, puisque les deux lignes suivantes sont exécutées le même nombre de fois que les lignes 369 et 370.

    La ligne 372 est en revanche très efficace pour réduire le nombre d'exécutions du code qui suit. Il est sans doute intéressant de faire le test sur $noeud_1 avec une regex immédiatement après le while(<FIC>) {, avant tout autre test, les actuelles lignes 369 à 371 seraient exécutées 410 fois moins souvent. J'ai donc essayé en ajoutant la ligne suivante après le while (<FIC>) de la ligne 368:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
        while(<FIC>) {
          next unless /$noeud_1/;
          last if ...
    la durée d'exécution tombe à 3 min 19, donc presque trois fois moins que la durée d'origine. Le gain commence à être réellement significatif.

    Mais surtout, est-il possible de ne pas avoir besoin de lire 141.384 fois le même fichier en entrée. Je ne comprends pas assez bien l'algorithme pour pouvoir le dire, c'est surtout toi qui peux te rendre compte. Là, si tu trouves moyen de réduire cela, la gain peut être considérablement meilleur.

  19. #19
    Expert confirmé

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2009
    Messages
    3 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Avril 2009
    Messages : 3 577
    Points : 5 753
    Points
    5 753
    Par défaut
    Citation Envoyé par Lolo78 Voir le message
    La vraie question à se poser: est-il possible de faire en sorte de ne pas avoir besoin d'exécuter 416.962.788 fois la ligne 379 (qui ne fait somme toute pas grand chose), 58.652.988 fois les lignes 369, 370, 371 et 372? A noter d'ailleurs que le last de la ligne 369 et le next de la ligne 370 ne servent en gros à rien, puisque les deux lignes suivantes sont exécutées le même nombre de fois que les lignes 369 et 370.
    Lors de la première lecture du fichier, il y a sans doute moyen de stocker en mémoire (dans un hash peut-être) les emplacements de chacune de ces lignes contenant "$EndNodeData" et par la même, ne pas avoir besoin de tester le contenu des lignes à ignorer, mais simplement comparer $. avec la position retenue en mémoire pour ce "NodeData".

    Pour la question de relire le fichier 140M fois, j'ai émis une hypothèse plus haut
    Plus j'apprends, et plus je mesure mon ignorance (philou67430)
    Toute technologie suffisamment avancée est indiscernable d'un script Perl (Llama book)
    Partagez vos problèmes pour que l'on partage ensemble nos solutions : je ne réponds pas aux questions techniques par message privé
    Si c'est utile, say

  20. #20
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France, Morbihan (Bretagne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 45
    Points : 29
    Points
    29
    Par défaut
    super le résultat du profilage. Dommage que ça ne marche pas chez moi.

    je suis en accord complet avec ce qui a été dit plus haut (sauf la relecture du fichier 140M de fois, j’y reviendrai plus bas). C’est impressionnant comment une mauvaise utilisation des regex peut conduire à une aussi grande chute de performance. Ce qui m’a étonné le plus, c’est le temps perdu avec les regex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (1) /^\s*\$EndNodeData\s*$/ et /^\s*\$NodeData\s*$/
    alors que mon intuition me disait que la regex suivante était la plus compliquée, donc la plus pénalisante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (2) /^\s*(\d+)\s+($format_reel)\s+($format_reel)\s+($format_reel)\s*$/
    Certes, le cout de la regex (1) est seulement d’environ 500 ns par appel contre 2 micro s pour la (2). Mais la (1) est appelée tellement de fois que ça en devient le boulet principal. Bluffant. Le pire, c’est que j’utilise énormément ces tournures dans mes scripts :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    while(<>) {
          last if(chaine de caracteres);
          next if(not /^\s*(\d+)\s+($format_reel) etc… $/);
          …
    }
    ou bien encore ça pour me positionner à un endroit d’un fichier :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    while(<>) {last if/chaine de caracteres/);}
    alors que c’est vraiment se tirer une balle dans le pied.

    Le next unless /noeud/ ainsi que l’utilisation de index comme proposé par Lolo78 me montre une nouvelle manière pour coder tout ça plus efficacement. Merci.

    Philou67430 :
    Concernant la lecture du fichier 140M de fois, j’ai effectivement constaté qu’une recherche par position $. stocké dans un hash était bien plus efficace que des regex (c’était l’objet de ma dernière modif, cf ma dernière pièce jointe, ce qui m’avait permis de passer de 4 mn à 2 mn 30 s en multi-threading). Je me demande si il n’y aurait pas moyen d’aller plus vite encore avec tell et seek.

    Philou67430 :
    Je ne sais pas si l'argument tient : si l'on parle bien du gros fichier $fgmsh_dpl, il est lu une première pour remplir le tableau @table_dpl_1, puis plusieurs fois pour renseigner des tableaux @liste_dpl_UX, @liste_dpl_UY et @liste_dpl_UZ, mais grosso modo, même si les tableaux @liste_dpl_U* ne contiennent que des sous-ensemble du tableau @table_dpl_1, ils semblent contenir les mêmes données.
    Je ne stocke jamais la totalité du fichier d’entrée dans @table_dpl_1. Dans l’ETAPE 3, je ne stocke jamais plus d’un ‘’NodeData’’ du fichier d’entrée, c’est-à-dire l’état de mes noeuds à un seul temps donné… et le fichier contient 1644 NodeData!!


    Néanmoins, cette relecture frénétique de ce fichier est un réel problème. Mais je me souviens pourquoi je n’avais pas le choix, car :
    1. Compte-tenu de la structure du fichier d’entrée, je ne peux pas écrire directement le fichier final en traitant tous les noeuds à la fois => je dois donc traiter chaque noeud séparément
    2. si je fais en une passe (en lisant une seule fois le fichier), je dois écrire les données pour chaque noeud dans des fichiers séparés (c’est de là qu’a émergé l’idée de plein de fichiers temporaires). Dans ma première mouture, j’ouvrais tous ces petits fichiers prêts pour l’écriture et je me suis heurté à la limite de handle. J’ai donc été obligé de renoncer. J’ai donc testé une stratégie d’ouverture pour ajout + fermeture de ces petits fichiers un grand nombre de fois, ce qui n’était pas efficace (à vue de nez car je n’avais pas de profilage pour me renseigner)
    3. la solution 2. n’étant pas satisfaisante, je me suis lancé dans la version actuelle que je vous ai donné. La nécessité de relire plein de fois le fichier d’entrée s’est imposée et j’ai conservé (sans doute à tord) l’idée de plein de petits fichiers temporaires. Mais ce n’était plus utile dans le cadre d’une relecture pour chaque noeud.
    4. la question de la création de plein de petits fichiers n’était pas un choix bien pensé dans le cadre de la solution 3., mais finalement, ça m’a permis de paralleliser. Tout est bien qui finit bien.

    Merci à vous. J’ai maintenant pas mal d’infos pour mieux réécrire mon code. Avec en plus le multi-threading, j’espère que ça va carburer. Et dire que le sujet de ce topic était de ne pas réécrire de scripts…
    N’hésitez pas à me faire part de votre expérience concernant tell et seek pour se positionner rapidement dans un fichier (efficace, pas efficace ??)

    ps : désolé pour le pavé…

Discussions similaires

  1. [Galerie] Un peu d'aide pour améliorer un script
    Par ambigua dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 1
    Dernier message: 21/02/2008, 22h32
  2. Appel d'offre pour une amélioration de script
    Par Hamzaxxx dans le forum Autres
    Réponses: 0
    Dernier message: 05/12/2007, 14h14
  3. access amélioration performance ouverture
    Par estebandelago dans le forum Access
    Réponses: 2
    Dernier message: 05/03/2007, 14h48
  4. Réponses: 6
    Dernier message: 23/01/2007, 17h20
  5. [MySQL] Amélioration performance requête
    Par lodan dans le forum PHP & Base de données
    Réponses: 15
    Dernier message: 15/01/2007, 09h06

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