Comment aller + vite ?

Comment aller + vite ? - Perl - Programmation

Marsh Posté le 10-01-2006 à 14:33:14    

Bonjour,
 
J'ai un pb d'optimisation :
 
je lis des infos d'une BDD via fetchall_arrayref que je stocke ds un tableau (table)
 
J'ai d'autre part un 2e tableau (applications) qui est issu d'une autre requete :
#print"APPLICATIONS : ".Dumper(@applications)."<BR>";
#$VAR1 = [ '1', '0', '', {}, {}, [] ];  
#$VAR2 = [ '1', '0', '', {}, {}, [] ];
La taille de @applications est bien sur tres gde (plusieurs milliers de lignes)
 
Le probleme reside ds le fait que je fais une bloucle for sur @applications pr chaque ligne de table
Comme table est tres gd (300 000 lignes), j'ai des perfs minables (plusieurs heures !!!)
Un for ds un for !!!!!
 
ex :
my $table = $sth_avg->fetchall_arrayref or die "$sth_appli->errstr\n";
for $i ( 0 .. $#{$table} )  
{
    print" ".Dumper($table->[$i])."<BR>";  
    #$VAR1 = [ '2176', '0', '2005-11-25 06:47:20.491787', '1', '0', '3478' ];
 
    for (my $k=0; $k<@applications; $k++)
    {
       if (( ($table->[$i]->[3] ne '') && ($table->[$i]->[3] eq $applications[$k]->[0]) ) ||  
    ( ($table->[$i]->[4] ne '') && ($table->[$i]->[4] eq $applications[$k]->[1]) ) ||
    ( ($table->[$i]->[5] ne '') && ($table->[$i]->[5] eq $applications[$k]->[2]) ))  
           {  
                ...
                last;
           }
    }
    ...
}
 
Comment aller + vite, passer par des hash ? comment faire ?
je suis pas un expert en PERL (loin de la)....
 
merci
 
   

Reply

Marsh Posté le 10-01-2006 à 14:33:14   

Reply

Marsh Posté le 10-01-2006 à 14:51:15    

et sans faire le fetchall mais plutot ligne par ligne ça va pas plus vite?
genre

Code :
  1. while ( $table = $sth->fetchrow_hashref() ){
  2. ...
  3. if (( ($table->[3] ne '') && ($table->[3] eq $applications[$k]->[0]) ) || 
  4.     ( ($table->[4] ne '') && ($table->[4] eq $applications[$k]->[1]) ) ||
  5.     ( ($table->[5] ne '') && ($table->[5] eq $applications[$k]->[2]) )) 
  6. ...
  7. }


ça t'evitera déjà de créer un tableau monstrueux en mémoire...

Reply

Marsh Posté le 10-01-2006 à 14:57:37    

merci d'avoir repondu.
je peux tester ca en effet.
la gestion memoire est allégée ms l'architecture reste identique et j'ai peur que cela soit mauvais...
 

Reply

Marsh Posté le 10-01-2006 à 15:22:09    

Sur le bout de traitement que tu montres là il va être difficile d'optimiser plus que ça...
Il faudrait voir ce que tu fais pour chaque résultat!!!
 
Maintenant je t'avoue que je ne comprends pas bien ton tableau applications...

Reply

Marsh Posté le 10-01-2006 à 16:01:21    

hum
au debut le tableau application ressemble a ca
$VAR1 = [ '1', '0', '4662', '43' ];
$VAR1 = [ '1', '0', '4118', '5' ];
$VAR1 = [ '1', '0', '4660', '5' ];  
 
de simple valeur, puis j'alloue un achage anonyme (j'avoue ignorer le but) :
my @tab=($tableOr->[$i]->[0],$tableOr->[$i]->[1],'',{},{},[]);
push(@applications,\@tab);
 
ce que je fais de chaque resultat ?, je garde le point commun des 2 tableau.
if (( ($table->[3] ne '')..........
{
   $ref_data=$applications[$k]->[3];
   %tmpData=%{$ref_data};
   $ident=$k;    
}
 
 
merci de ton aide.

Reply

Marsh Posté le 10-01-2006 à 16:18:23    

Tu peux pas remonter que les bons enregistrements en une requête?
Parce que si j'ai tout compris tes deux tableaux sont le résultat de deux requêtes séparées.

Reply

Marsh Posté le 10-01-2006 à 16:33:33    

oui, 2 requetes separées.
 
c possible ms les requetes st assez complexes, la 1ere a 6 niveaux de profondeur et la seconde 4.
dc pas facile de bien comprendre le pourquoi du comment (pas une ligne de doc), dc merger les 2 me semble fou !
6 niveaux de profondeur sur une requete postgres est a mon avis une erreur de conception.

Reply

Marsh Posté le 10-01-2006 à 16:47:28    

Bin c'est pas gagné là :o
 
si tu as deux tableaux énormes et que tu es obligé de faire l'intersection "à la main" forcément ça sera long.
As-tu chercher sur la CPAN si il existait un module "optimisé" pour faire ça?
 
Reste la solution ultime, acheter un serveur 10 fois plus puissant ;)

Reply

Marsh Posté le 10-01-2006 à 17:22:13    

:-)
 
voui, je cherche un truc style List::Compare.....
c la piste qu'il me reste....
 
la possibilite hard est enviseagee ms ce n'est que reculer !

Reply

Marsh Posté le 10-01-2006 à 17:38:42    

deja kand tu parcours un tableau mieux vaut faire un for(@application) qu'un for(;;) à la C, ca va souvent beaucoup plus vite (quit à incrementer un variable à chaque iteration pour rester synchrone avec une autre tableau).
 
ensuite effectivement le mieux est sans doute de faire une hash à partir de ton tableau @applications, pour répondre en temps constant au test que tu fais à chaque ligne de @table
 
deja tu peux virer tes test genre "$table->[$i]->[3] ne ''" en mettant un petit "no warnings" dans ton premier for (il ne geulera pas si la valeur est indéfinie).
 
en gros tu fais ton traitement quand les elements 3,4,5 de table sont egaux au element 0,1,2 de application
 
ce que je te conseil c'est de faire ca:
avant de faire ta seconde requete tu crais une hash avec comme clé la concatenation des elements 0,1,2 et comme valeur le rang dans @application (ou meme plutot une reference directe vers le tableau pointé).
ensuite dans la boucle sur les row de ta deuxiemme requete tu fais la concatenation des elements 3,4,5 ton row et tu test ta hash table. si ca match tu fais ton traitement
 
en gros :

Code :
  1. my %application_hash = map{join(" - ", @$_[0,1,2]) => $_} @applications;
  2. # seconde requete => $table
  3. for my $row (@$table) {
  4.   my $cle = join(" - ", @$row[3,4,5]);
  5.   if (my $ref = $application_hash{$cle}) {
  6.     ...
  7.   }
  8. }

Reply

Marsh Posté le 10-01-2006 à 17:38:42   

Reply

Marsh Posté le 11-01-2006 à 01:33:49    

meme si y'a 2 requêtes, pas moyen d'ajouter une condition à chacune pour ne pas récupérer les entrées dont la 4e ([3]) colonne n'est pas vide?

Reply

Marsh Posté le 11-01-2006 à 08:21:49    

merci pr les posts.
je vais tester ca de suite, et comprendre avant !

Reply

Marsh Posté le 11-01-2006 à 10:57:21    

je comprends tt sauf  
if (my $ref = $application_hash{$cle}) {...}
a quoi est egale ref ?, je sais que c le hash de application avec une cle particuliere, ms comment tracer son contenu ?
la boucle for (my $k=0; $k<@applications; $k++) ds le for $i ( 0 .. $#{$table} ) disparait ?
 
ds mon traitement final, je fais $ref_data=$applications[$k]->[3]; or
applications est devenu un hash specifique....dc comment faire ?
 
ps : je ne suis pas un king en Perl ms j'aime apprendre ....
 
merci

Reply

Marsh Posté le 11-01-2006 à 11:43:56    

$ref vaut $application_hash{$cle} si il existe une valeur dans application_hash pour $cle sinon le traitement dans le if n'est pas effectué.
 
Pour ton ref_data, tu as toujours ta ligne du tableau dans la valeur de ta hash associé à le clef.
 
ps: tu peux le dire si je suis pas clair ça m'étonnerait pas :o

Reply

Marsh Posté le 11-01-2006 à 11:53:25    

"$ref vaut $application_hash{$cle} si il existe une valeur dans application_hash pour $cle sinon le traitement dans le if n'est pas effectué. "
ok je suis d'accord, comment le voir, le tracer, j'y arrive pas !
 
"Pour ton ref_data, tu as toujours ta ligne du tableau dans la valeur de ta hash associé à le clef."
okkkkkkkkkkk, ca va mieux, cela ss entend que je garde la boucle for (my $k=0; $k<@applications; $k++)

Reply

Marsh Posté le 11-01-2006 à 12:54:13    

non non, ta boucle gicle
 
en fait ref n'est pas la ligne du tableau mais la reference directe du tableau pointé par l'entrée de @application (putain je suis pas clair!!)
 
en gros @application est un tableau de tableau, un tableau de reference vers d'autres tableaux en fait.
 
mais si tu prefere on peut mettre le rang dans application à la place de la ref directe vers le contenu si tu prefere
 
dans ce cas il faut changer un peu le map{} du debut:

Code :
  1. my $i=-1;
  2. my %application_hash = map{join(" - ", @$_[0,1,2]) => ++$i} @applications;


 
et ensuite au lieu de tester if($ref = ...) tu test if (defined $rank = ...)
 

Reply

Marsh Posté le 11-01-2006 à 13:20:18    

"a reference directe du tableau pointé par l'entrée de @application"
ok je comprend meiux comme ca.
ok pr @application
ok je teste ca....
c cool merci
 
je vais y arriver !

Reply

Marsh Posté le 11-01-2006 à 13:39:23    

Ok je ne reponds pas a la question mais c'est juste pour rigoler !  :na:  

Code :
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. #attention $max = 10000000 => un process de 680Mo
  5. my $max = 10000000;
  6. my @tab = 0..$max;
  7. my $time = {};
  8. my $r = 0;
  9. sub printtime {
  10.   my ($loop) = @_;
  11.   print "loop $max '$loop' time: ";
  12.   print $time->{$loop}->{end} - $time->{$loop}->{start}, "s\n";
  13. }
  14. $time->{for}->{start} = time();
  15. for (my $i = 0; $i <= $#tab; $i++) {
  16.   $r += $tab[$i];
  17. }
  18. $time->{for}->{end} = time();
  19. $r = 0;
  20. $time->{for_sca}->{start} = time();
  21. for (my $i = 0; $i < @tab; $i++) {
  22.   $r += $tab[$i];
  23. }
  24. $time->{for_sca}->{end} = time();
  25. $r = 0;
  26. $time->{foreach}->{start} = time();
  27. foreach my $elm (@tab) {
  28.   $r += $elm;
  29. }
  30. $time->{foreach}->{end} = time();
  31. $r = 0;
  32. $time->{map}->{start} = time();
  33. map {$r += $_} (@tab);
  34. $time->{map}->{end} = time();
  35. $r = 0;
  36. $time->{while}->{start} = time();
  37. while (@tab) {
  38.   $r += shift @tab;
  39. }
  40. $time->{while}->{end} = time();
  41. printtime('for');
  42. printtime('for_sca');
  43. printtime('foreach');
  44. printtime('while');
  45. printtime('map');


 


bash-2.05b$ ./for-ou-foreach.pl
loop 10000000 'for' time: 11s
loop 10000000 'for_sca' time: 7s
loop 10000000 'foreach' time: 4s
loop 10000000 'while' time: 8s
loop 10000000 'map' time: 8s
bash-2.05b$ ./for-ou-foreach.pl
loop 10000000 'for' time: 11s
loop 10000000 'for_sca' time: 8s
loop 10000000 'foreach' time: 4s
loop 10000000 'while' time: 6s
loop 10000000 'map' time: 5s
bash-2.05b$ ./for-ou-foreach.pl
loop 10000000 'for' time: 12s
loop 10000000 'for_sca' time: 8s
loop 10000000 'foreach' time: 4s
loop 10000000 'while' time: 8s
loop 10000000 'map' time: 4s
bash-2.05b$ ./for-ou-foreach.pl
loop 10000000 'for' time: 12s
loop 10000000 'for_sca' time: 8s
loop 10000000 'foreach' time: 4s
loop 10000000 'while' time: 8s
loop 10000000 'map' time: 4s
bash-2.05b$ ./for-ou-foreach.pl
loop 10000000 'for' time: 13s
loop 10000000 'for_sca' time: 8s
loop 10000000 'foreach' time: 4s
loop 10000000 'while' time: 8s
loop 10000000 'map' time: 4s


 
Si les valeurs ont change, les proportions sont presques les memes.
 
Vive les trolls !


Message édité par Danjer le 11-01-2006 à 13:56:53

---------------
Cdl, Danjer
Reply

Marsh Posté le 11-01-2006 à 13:47:49    

c tjs bien d'avoir un ordre d'idee !

Reply

Marsh Posté le 11-01-2006 à 13:53:15    

pr resumer, g fais ca :
 
my $i=-1;
my %application_hash = map{join(" - ", @$_[0,1,2]) => ++$i} @applications;
 
for my $row (@$table)
{
   my $volume= @$row[0];
   ....
 
   my $cle = join(" - ", @$row[3,4,5]);
   if (my $rank = $application_hash{$cle})   #<--
   {
      $ref_data=$applications[$rank]->[3];
      %tmpData=%{$ref_data};
      $ident=$k;
   }
}
 
cela ne marche pas, je ne passe pas ds le if ! que ce soit avec my $rank ou bien defined $rank
j'ai l'impression d'etre un boulet par moment !

Reply

Marsh Posté le 11-01-2006 à 15:14:42    

dump \%application_hash pour voir ce qu'il a dans le ventre, et afficher la cle à chaque iteration aussi.
Il doit y avoir une couille qqpart. A vu de nez ca a l'air bon pourtant.
t'es sur que tu veux comparer 0,1,2 et 3,4,5 ?
 
le define c'est juste pour que ca match meme quand le rank est nul. tu peux aussi utiliser exists, ca sera pareil

Reply

Marsh Posté le 11-01-2006 à 15:15:54    

juste pour que tu vois, si on avait mis la ref au lieu du rank (comme dans le premier truc que je t'avais filé) ca aurai donné ca dans ton if :
 
$ref_data = $ref->[3]
 
au lieu de  
$ref_data=$applications[$rank]->[3];

Reply

Marsh Posté le 11-01-2006 à 15:18:06    

a noter que les mots clé for et foreach sont interchangeable
donc ecrire for(@a) c'est exactement pareil que foreach(@a)

Reply

Marsh Posté le 11-01-2006 à 15:29:30    

je suis d'accord avec les ref_data....c plus clair avec ton exemple.
je pense avoir trouve mon pb :  
mon if original possede des || :  
"if (( ($table->[$i]->[3] ne '') && ($table->[$i]->[3] eq $applications[$k]->[0]) ) ||  
( ($table->[$i]->[4] ne '') && ($table->[$i]->[4] eq $applications[$k]->[1]) ) ||
( ($table->[$i]->[5] ne '') && ($table->[$i]->[5] eq $applications[$k]->[2]) ))  
{...}
 
et je pense que la cle "$cle = join(" - ", @$row[3,4,5]);" prend les 3 elements ensemble et non de facon separee...
dc je ne passe js ds mon if car les 3 cas ne st jamais verife !
ok avec moi ?
 
merci de ton aide

Reply

Marsh Posté le 11-01-2006 à 17:19:11    

ha ok putain exacte!
j'ai pas percuté, je voyais des &&
 
donc dans ce cas il faut que tu fasse 3 entrée dans $application_hash pour chaque entrée de @application
mais comme tu veux matcher 0 avec 3 et non avec les autres il faut que tu differentie les clé qui viennent de 0,1 et 2
 
donc soit tu fait trois hash, soit tu ajoute un marqueur au debut de ta clé. on va prendre la premiere solution pour l'exemple
 

Code :
  1. my %first_hash = map{$_->[0] => $_} @applications;
  2. my %second_hash = map{$_->[1] => $_} @applications;
  3. my %third_hash = map{$_->[2] => $_} @applications;
  4. # seconde requete => $table
  5. for my $row (@$table) {
  6.   if (my $ref = $first_hash{$row->[3]} || $second_hash{$row->[4]} || $third_hash{$row->[5]}) {
  7.     ...
  8.   }
  9. }

Reply

Marsh Posté le 11-01-2006 à 17:27:05    

yes t'es mon ami :-)
 
c ce que j'ai fais !!!, ms un truc ne colle pas, je creuse !
 
bon ca avance, je te tiens au courant
 
tks

Reply

Marsh Posté le 12-01-2006 à 10:30:14    

c bon , ca marche !
 
youpi.
 
merci a ts le monde.

Reply

Marsh Posté le 13-01-2006 à 10:41:27    

ca passe en combien de temps maintenant?

Reply

Marsh Posté le 13-01-2006 à 17:23:26    

j'ai gagne 40 % de tps, c pas mal ms peut meiux faire.
en faire c la 1e boucle qui prend du tps !
les petits traitements ds la boucle st minimes.
 
d'ou un axe de reflexion... :
reecrire/compiler cette boucle en C ?, dur a faire ?
compiler le code PERL pr aller + vite ?
 
quelle est la solution pr avoir le gain de tps le + gd ?
 
.....
bon we.
 

Reply

Marsh Posté le 13-01-2006 à 17:29:52    

t'es sur que c'est ta boucle qui rame? c'est pas la requete SQL ?
 
refaire ca en C c'est chiant
compiler le Perl c'est quasi impossible (ya un compilateur qui ne marche pas, à par sur les cas ultra simple, et qui de toutes facon ne fait pas gagner en vitesse à part au lancement, et il y a des packagers de code qui ne font pas gagner de vitesse nonplus)
 
300 000 lignes c'est pas enorme pourtant...
t'es sur que c'est la boucle qui rame? ca prend combien de temps
balance le code exacte de ce que tu fais dedans, on peu peut etre gratter un peu

Reply

Marsh Posté le 13-01-2006 à 17:51:13    

non j'ai pris le tps. la requete fait ds les 4s.
tt est mis en memoire via fetchall_arrayref.
 
ok, j'ai enleve les hash car la 2e boucle for sortait des la 1ere iteration
dc le gain n'etait pas significatif.
 
voici la methode complete.....
sub traficApplicatifEmissionReception {
 
 my ($this)=shift;
 my ($debut,$fin,$precision,$requete_appli,$requete_avg,$title)=@_;  
 my $writer=new LogSecureWriter;
 my $path;
 
 #print "debut : $debut<BR>";
    #print "fin : $fin<BR>";
    #print "precision : $precision<BR>";
    #print "requete_appli : $requete_appli<BR>";
    #print "requete_avg : $requete_avg<BR>";
    #print "title : $title<BR>";
 
 #Exemple de date a fournir en parametre a la Methode
 
 #$debut = DateTime->new(  year   => 2004,
    #        month  => 5,
    #        day    => 14,
    #        hour   => 00,
    #        minute => 00,
    #        second => 47,
    #      );
 
 ##############################
 #Configuration du graph   #
 # --> Parametres modifiables #
 ##############################
 
 #Resolution du graph en pixels
 
 my $graph = GD::Graph::area->new(1024,768);  # resolution : 1024*768 Pixels
 
 #Interval des Affichage des dates en X
 #Sur un graph en 1024*768 on peu avoir 40 labels en X maximum , apres sa devien illisible
 
 my $interval_xLabel=20; #20 est une bonne valeur
 
 #on fera une requete tout les interval_requete interval
 
 my $intervalle_calcul = $precision;
 
 #####################  
 #Calcul de l'echelle#
 #####################
 
 
 my $duree_second=$fin->subtract_datetime_absolute($debut); #Retourne uniquement des secondes
 
  #DEBUG: print"<BR>----DEBUT  : ".Dumper($debut)."<BR>";
  #DEBUG: print"<BR>----FIN : ".Dumper($fin)."<BR>";
  #DEBUG: print"<BR>----duree : ".Dumper($duree_second)."<BR>";
 
 my $interval= DateTime::Duration->new( years   => 0,
              months  => 0,
                 weeks   => 0,
                    days    => 0,
                       hours   => 0,
           minutes => 0,
           seconds => $intervalle_calcul,  
           nanoseconds => 0 );
 
 #my $moitier_interval= DateTime::Duration->new( years   => 0,
     #                           months  => 0,
      #                          weeks   => 0,
       #                         days    => 0,
        #                        hours   => 0,
         #                       minutes => 0,
           #                     seconds => ($intervalle_calcul/2),  
            #                    nanoseconds => 0 );
 
 #####################################################
 #Recuperation des 10 Applications les plus utilisées#
 #####################################################
 
 my @applications;
 my %autres;
 my %tabAutres;
 my @finalAutres;
 my %hashData;
 my %application_hash;
    my %first_hash;
    my %second_hash;
    my %third_hash;  
    my %fourthly_hash;  
    my %fifthly_hash;
 
 my $bddinjector=new BddInjector;
 
 if(!(my $dbh=$bddinjector->BddConnect)){ #Connection a la base
  $writer->secureAppend("BDD_ERROR.LOG","la méthode traficApplicatifEmissionReception() de l'objet".__PACKAGE__." a échoué: impossible de se connecter à la base de données :$DBI::errstr.\n" );
  return 0;
 }else{
 
  # /!\ Utilisation de la requete passé en parametre /!\
   
  my $sth_appli=$dbh->prepare($requete_appli);
 
  #print "REQ :<BR>$requete_appli<BR>";
 
  if(!($sth_appli->execute())){
   #echec de l'execution de la requête
   $writer->secureAppend("BDD_ERROR.LOG","dans la méthode traficApplicatifEmissionReception() de l'objet".__PACKAGE__.": échec de la requête : $requete_appli.$DBI::errstr.\n" );
   return 0;
  }else{
   
            #cdd+
            $secondesAP = $this->GetTimeSecondes();
         $DeltaT = $secondesAP - $secondesAV;
         print "<FONT SIZE=3 COLOR=\"red\">requete_appli $DeltaT sec<BR></FONT>";
         #cdd-  
 
      $secondesAV = $this->GetTimeSecondes();#cdd  
       
            #Cette méthode est utilisée pour faire en sorte que toutes les  
            #données (lignes) soient retournées par la requête SQL.  
            #Elle retourne une référence à un tableau de références à des  
            #tableaux pour chaque ligne.  
      my $table = $sth_appli->fetchall_arrayref or die "$sth_appli->errstr\n";
            #cdd+
            $secondesAP = $this->GetTimeSecondes();
         $DeltaT = $secondesAP - $secondesAV;
         print "<FONT SIZE=3 COLOR=\"red\">fetchall_arrayref1 $DeltaT sec<BR></FONT>";
         #cdd-
            $secondesAV = $this->GetTimeSecondes();#cdd    
               
            my $i_= 0;    
      my $i = 0;
            my $j;
       
            #for $i ( 0 .. $#{$table} )  
            foreach my $row (@$table)
            {
                #print Dumper(@$row[0])."-".Dumper(@$row[1])."-".Dumper(@$row[2])."-".Dumper(@$row[3])."<BR>";  
                 
    if (@$row[3] >= 1)  
                {  
                    #Limite minimum 1%
 
     if ((@$row[0] eq '') and (@$row[1] eq ''))  
                    {  
                        #print "Application NON REFERENCEE<BR>";
      #$first_hash{''}       = $i;
      #$second_hash{''}      = $i;
      #$third_hash{@$row[2]} = $i;
       
      my @tab=('','',@$row[2],{},{},[]); #On stock donc le port_destination
      push(@applications,\@tab);
     }
                    else
                    {
                        #print "Application REFERENCEE<BR>";
                        #cdd Le {} alloue un hachage anonyme ne contenant aucune paire clé/valeur, et le retourne.
                        #[] et {} st des constructeurs anonymes
       
      #$first_hash{@$row[0]}  = $i;
      #$second_hash{@$row[1]} = $i;
      #$third_hash{''}        = $i;
       
      my @tab=(@$row[0],@$row[1],'',{},{},[]);  
                        push(@applications,\@tab);
                                                     
     }    
    }
    #$third_hash{@$row[2]} = $i;  
    $i++;
            }
                       
            #cdd+
            $secondesAP = $this->GetTimeSecondes();
         $DeltaT = $secondesAP - $secondesAV;
         print "<FONT SIZE=3 COLOR=\"red\">traitement fetchrow_arrayref 1 en $DeltaT sec<BR></FONT>";
         #cdd-  
  }
  #print"APPLICATIONS : ".Dumper(@applications)."<BR>";
 
  $sth_appli->finish;
   
  ###################################
  #Construction du tableau de donnée#
  ###################################
 
  my @xLabels;          #Tableau des labels
  my $maxValue=1;    #Valeur Maximum de l'axe Y
 
  my $j=0; #Compteur pour l'affichage des Labels en X
  my $z=0;
  #my $i=0;
   
  $secondesAV = $this->GetTimeSecondes();#cdd
 
  my $requete = $requete_avg;
 
  #print "<BR><BR>DATA : $requete<BR><BR>";
 
  my $sth_avg=$dbh->prepare($requete);
 
  if(!($sth_avg->execute())){
   #echec de l'execution de la requête
   $writer->secureAppend("BDD_ERROR.LOG","dans la méthode traficApplicatifEmissionReception() de l'objet".__PACKAGE__.": échec de la requête : $requete.$DBI::errstr.\n" );
   return 0;
  }
   
        #cdd+
        $secondesAP = $this->GetTimeSecondes();
     $DeltaT = $secondesAP - $secondesAV;
     print "<FONT SIZE=3 COLOR=\"red\">requete_avg $DeltaT sec<BR></FONT>";
     #cdd-
   
  ######################################
  $secondesAV = $this->GetTimeSecondes();#cdd
 
     my $table = $sth_avg->fetchall_arrayref or die "$sth_appli->errstr\n";
     
        #cdd+
        $secondesAP = $this->GetTimeSecondes();
     $DeltaT = $secondesAP - $secondesAV;
     print "<FONT SIZE=3 COLOR=\"red\">fetchall_arrayref2 $DeltaT sec<BR></FONT>";
     #cdd-      
     $secondesAV = $this->GetTimeSecondes();#cdd
     my($i, $j);
     
        #for $i ( 0 .. $#{$table} )  
         
        foreach my $row (@$table)
        {
            #print" ".Dumper($table->[$i])."<BR>";  
            #Ligne : $VAR1 = [ '2176', '0', '2005-11-25 06:47:20.491787', '1', '0', '3478' ];
             
            #print "row[0-1-2-3-4-5] : @$row[0]-@$row[1]-@$row[2]-@$row[3]-@$row[4]-@$row[5]<BR>";
             
             
            $my_i++;
            $secondesAV1 = $this->GetTimeSecondes();#cdd
   ############################
   #Determination de l'interval
 
   my $volume = @$row[0];#$table->[$my_k]->[0];  
   my $duree  = @$row[1];#$table->[$my_k]->[1];
 
   #$table->[$my_k]->[2] =~ /^(\d*)-(\d*)-(\d*)\s(\d*):(\d*):(\d*)\.?.*$/i ; #Date_debut
   @$row[2] =~ /^(\d*)-(\d*)-(\d*)\s(\d*):(\d*):(\d*)\.?.*$/i ; #Date_debut
 
   my $A= DateTime->new(year  => $1,
         month => $2,
         day   => $3,
         hour  => $4,
         minute=> $5,
         second=> $6,
         );
 
   my $interval_flux_seconde= $A->subtract_datetime_absolute($debut);
   my $interval_flux= $interval_flux_seconde->seconds;
 
   #DEBUG:print "--Interval Flux : $interval_flux  <BR>";
 
   ###########################
   #Saut en cas de dépassement
   
   if ($interval_flux>($duree_second->seconds)) {
    next;
   }
   
   ###############################
   #Determination de l'application
 
   my $id = 0;
   my $ref_data;
   my %tmpData;
   my $ident = "autre";
   
                   
            #print ("table->[$i]->[3] : @$row[3]<BR>" );
            #print ("table->[$i]->[4] : @$row[4]<BR>" );
            #print ("table->[$i]->[5] : @$row[5]<BR><BR>" );
             
            #for (my $i = 0; $i <= $#tab; $i++) {$r += $tab[$i];}
            #foreach my $elm (@tab) {$r += $elm;}
               
            foreach my $elm (@applications)                    
            #for (my $k=0; $k<@applications; $k++)
            {
                #Correction 5/11/04 : plus de test sur "ligne[x] ne 0" --> non prise en compte des donnée quand port_appli==0
                if (( (@$row[3] ne '') && (@$row[3] eq $elm[0]) ) ||  
                  ( (@$row[4] ne '') && (@$row[4] eq $elm[1]) ) ||
                 ( (@$row[5] ne '') && (@$row[5] eq $elm[2]) ))  
                {      
              #Ce flux appartient bien a cette application
              #On ajoute donc les données de ce flux
              $ref_data=$applications[$k]->[3];
              %tmpData=%{$ref_data};
              $ident=$k;
              #Ajout 8/11/04 : evite de continuer a chercher l'application quand elle a ete trouvée
              last;
                }
   }  
               
            ##################################
   #Ajout des donnés suivant la duree
             
            #print "interval_flux($interval_flux) Volume($volume) ident($ident)<BR>";
   if ($duree == 0)  
            {  
                #La durée du flux est nulle
    if ($ident eq "autre" )  
                {
     $autres{$interval_flux}+=$volume;
    }
                else
                {
     $tmpData{$interval_flux}+=$volume;
     $applications[$ident]->[3]=\%tmpData;
    }
   }
            else
            {
            #for (my $i = 0; $i <= $#tab; $i++) {$r += $tab[$i];}
            #foreach my $elm (@tab) {$r += $elm;}
             
                #print "duree : $duree";
                foreach $i (0 .. $duree)
    #for(my $i=0;$i<$duree;$i++)
                {
     if (not( (($interval_flux+$i)>($duree_second->seconds)) ) )
                    {  
                        #Si pas de depassement
      if ($ident eq "autre" )  
                        {
       $autres{$interval_flux+$i}+=$volume/$duree;
      }
                        else
                        {
       $tmpData{$interval_flux+$i}+=$volume/$duree;
       $applications[$ident]->[3]=\%tmpData;
      }
     }
    }
   }
   
            #print" ".Dumper($applications[$ident]->[0]);
            #print" ".Dumper($applications[$ident]->[1]);
            #print" ".Dumper($applications[$ident]->[2]);
            #print" ".Dumper($applications[$ident]->[3]);
            #print" ".Dumper($applications[$ident]->[4])."<BR><BR><BR>";    
            $my_k++;
        }        
   
        #cdd+
        $secondesAP = $this->GetTimeSecondes();
     $DeltaT = $secondesAP - $secondesAV;
     print "<FONT SIZE=3 COLOR=\"red\">traitement fetchrow_arrayref2[$my_i] en $DeltaT sec<BR></FONT>";
     $my_i = 0;
     #cdd-    
 
  $sth_avg->finish;
 
        $secondesAV = $this->GetTimeSecondes();#cdd
 
  ##########################################################
  #Creation des tableaux de donnée pour l'interval de calcul
   
  #for (my $k=0; $k<@applications; $k++){$applications[$k]->[0]}  #$elm[0] = $applications[$k]->[0]
 
        foreach my $elm (@applications){ #Pour chaque application  
  #for (my $k=0; $k<@applications; $k++){ #Pour chaque application
 
   my $ref_dataSet = $elm[3];
   my %dataSet = %{$ref_dataSet};
 
   my $ref_tab = $elm[4];
   my %tab = %{$ref_tab};
 
   for(my $i=0;$i<($duree_second->seconds);$i=$i+$intervalle_calcul) {
    for(my $j=$i;$j<$i+$intervalle_calcul;$j++) {  
     
     if (defined($dataSet{$j})) {
      $tab{$i}+=(($dataSet{$j})/($intervalle_calcul));
     }else{
      $tab{$i}+=0;
     }
    }
   }
   $elm[4] = \%tab;
  }
     
  for(my $i=0;$i<($duree_second->seconds);$i=$i+$intervalle_calcul) {
   for(my $j=$i;$j<$i+$intervalle_calcul;$j++) {
    if (defined($autres{$j})) {
     $tabAutres{$i}+=(($autres{$j})/($intervalle_calcul));
    }else{
     $tabAutres{$i}+=0;
    }
   }
  }
 
 
  #on balance dans le jeu de données
  foreach my $elm (@applications) {#Pour chaque application
  #for (my $k=0; $k<@applications; $k++){$applications[$k]->[0]}  #$elm[0] = $applications[$k]->[0]
  #for (my $k=0; $k<@applications; $k++){ #Pour chaque application
 
   my $ref_dataSet = $elm[4];
   my %dataSet = %{$ref_dataSet};
 
   my $ref_finalSet = $elm[5];
   my @finalSet = @{$ref_finalSet};
 
   for(my $i=0;$i<($duree_second->seconds);$i=$i+$intervalle_calcul) {
    push(@finalSet,$dataSet{$i});
   }
   $elm[5] = \@finalSet;
  }
 
  for(my $i=0;$i<($duree_second->seconds);$i=$i+$intervalle_calcul) {
    push(@finalAutres,$tabAutres{$i});
  }
 
  ##########################
  #Construction de l'echelle
   
  my $cpt=0;
 
        #foreach $i (0 .. (($duree_second->seconds)/$intervalle_calcul)){
  for(my $i=0;$i<(($duree_second->seconds)/$intervalle_calcul);$i++) {
 
   if ($cpt == $interval_xLabel ) {
    $cpt=0;
    my $tempLabel = $debut+ ($i*$interval);
    $xLabels[$i]=$tempLabel->strftime("%H:%M:%S" );
   }else{
    $xLabels[$i]='';
   }
 
   $cpt++;
   
  }
 
  ################################################################################
  #Remplacement du Numero d'appli par le nom et construction du tableau de donnée#
  ################################################################################
 
  my @data;    #Les donnée a tracer
  my @legende; #Les legendes du graph
 
  #Tableau contenant les donnée a tracer sur le graph
  push (@data,\@xLabels);
 
  #Construction du tableau Data qui contiendra les donnée du graph a partir du tableau Application
 
        foreach my $elm (@applications) {
  #for(my $i=0;$i<(@applications);$i++) {
 
   my $requete_nom;
   my $unknow=0;
 
   if (($elm[0]==0) and ($elm[1]==0)) {  #Affichera le numero de port destination
 
    push(@legende,"[inconnue] port : $elm[2]" );  
 
    $unknow=1;
     
   }elsif ($elm[0]==0){        #Affichera le nom d'appli speciale
 
    $requete_nom = " SELECT application_spe.nom,application_spe.port FROM application_spe";
    $requete_nom .= " WHERE application_spe.fk_analyses_id=$this->{INDEX_ANALYSE} ";
    $requete_nom .= " AND application_spe.id=$elm[1] ";
 
   }else{             #Affichera le nom d'appli par default
 
    $requete_nom = " SELECT application.nom,application.port FROM application ";
    $requete_nom .= " WHERE application.id=$elm[0] ";
 
   }
 
   if ($unknow!=1) {
 
    my $sth_nom=$dbh->prepare($requete_nom);
 
    if(!($sth_nom->execute())){
     #echec de l'execution de la requête
     $writer->secureAppend("BDD_ERROR.LOG","dans la méthode traficApplicatifEmissionReception() de l'objet".__PACKAGE__.": échec de la requête : $requete_nom.$DBI::errstr.\n" );
     return 0;
    }else{
 
                    while(my $ligne=$sth_nom->fetchrow_arrayref){
     #while(my @ligne=$sth_nom->fetchrow_array){
      push(@legende,"$ligne->[0] ($ligne->[1])" );  
     }
    }
 
    $sth_nom->finish;
   }
 
   push (@data,$elm[5]); #Donnée pour chaque application
 
   my @verif=@{$elm[5]};
   #print "<BR><BR>***Nb Elem : $#verif***<BR><BR>";
 
  }
 
  #On met les applications autres ( id = 0 )
  push (@data,\@finalAutres);
 
  push(@legende,'Autres');
   
  $dbh->disconnect;
 
  ###########################
  #Construction du Graphique#
  ###########################
 
  #DEBUG: print"FINAL DATA : ".Dumper(@data)."<BR><BR>";
  #DEBUG: print"LEGENDE  DATA : ".Dumper(@legende)."<BR><BR>";
 
   
  #Preparation de l'object Graph
   
  $graph->set(
  x_label     => "Horaires",
  y_label     => ' Bit / seconde',
  title       => "$title",
  x_label_skip   => 1,
  x_labels_vertical => 1,
  legend_placement =>'BL',
  long_ticks    => 0,
  x_ticks   =>0,
  cumulate  =>1,
  fgclr   =>'black',
  accentclr  =>'black',
  labelclr  =>'black',
  valuesclr  =>'black',
  axislabelclr =>'black',
  boxclr   => 'white',
  dclrs   =>['marine','orange','pink','cyan','yellow','lblue','lgray','purple','lgreen','red','lbrown'],
  l_margin =>10,
  r_margin =>10,
  y_min_value =>0,
#  y_max_value =>int($maxValue),
  ) or warn $graph->error;
 
  #Couleur predefinies :
  #white, lgray, gray, dgray, black, lblue, blue, dblue, gold,  
  #lyellow, yellow, dyellow, lgreen, green, dgreen, lred, red,  
  #dred, lpurple, purple, dpurple, lorange, orange, pink, dpink,  
  #marine, cyan, lbrown, dbrown.
 
  $graph->set_legend(@legende);
  $graph->set_legend_font(GD::Font->MediumBold);
  $graph->set_title_font(GD::Font->MediumBold);
  $graph->set_x_axis_font(GD::Font->MediumBold);
  $graph->set_y_axis_font(GD::Font->MediumBold);
  $graph->set_x_label_font(GD::Font->MediumBold);
  $graph->set_y_label_font(GD::Font->MediumBold);
 
  #Tracage du graph
 
  #Enregistrement du graph dans un fichier
   
  my $file=$title;
 
  $file=~ s/(\s)/_/g; #On remplace les escape dans le $title par des _
 
  my $path="/$this->{IMG_PATH}/$file.png";
   
  open(IMG, ">$path" );
  binmode IMG;
  print IMG $graph->plot(\@data)->png or die $graph->error;
  close IMG;
 
  #Convertion du Png en Jpg
   
  my $filename=$this->png2jpg($path);
 
  $writer->secureAppend("GRAPHS.LOG"," -->  Creation de $filename \n" );
   
        #cdd+
        $secondesAP = $this->GetTimeSecondes();
     $DeltaT = $secondesAP - $secondesAV;
     print "<FONT SIZE=3 COLOR=\"red\">reste du traitement en $DeltaT sec<BR></FONT>";
     #cdd-  
   
  return $filename;
 }
}
 
en entree j'ai ca :  
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '4662'; -$VAR1 = '57';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '4660'; -$VAR1 = '4';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '7039'; -$VAR1 = '3';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '12896'; -$VAR1 = '2';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '4024'; -$VAR1 = '2';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '4221'; -$VAR1 = '2';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '7561'; -$VAR1 = '2';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '4118'; -$VAR1 = '2';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '3938'; -$VAR1 = '1';
$VAR1 = '1'; -$VAR1 = '0'; -$VAR1 = '7812'; -$VAR1 = '1';
traitement fetchrow_arrayref 1 en 0 sec
APPLICATIONS1 :  
$VAR1 = [ '1', '0', '', {}, {}, [] ];  
$VAR2 = [ '1', '0', '', {}, {}, [] ];  
$VAR3 = [ '1', '0', '', {}, {}, [] ];  
$VAR4 = [ '1', '0', '', {}, {}, [] ];  
$VAR5 = [ '1', '0', '', {}, {}, [] ];  
$VAR6 = [ '1', '0', '', {}, {}, [] ];  
$VAR7 = [ '1', '0', '', {}, {}, [] ];  
$VAR8 = [ '1', '0', '', {}, {}, [] ];  
$VAR9 = [ '1', '0', '', {}, {}, [] ];  
$VAR10 = [ '1', '0', '', {}, {}, [] ];
requete_avg 4 sec
fetchall_arrayref2 0 sec
row[0-1-2-3-4-5] : 3904-0-2005-11-25 06:54:06.662454-708-0-500
row[0-1-2-3-4-5] : 0-0-2005-11-24 19:17:00.185121-1-0-4672
row[0-1-2-3-4-5] : 2176-0-2005-11-25 06:44:13.653105-1-0-3478
row[0-1-2-3-4-5] : 5504-0-2005-11-24 17:09:41.142196-119-0-80
300 000 lignes comme ca.
 
merci de ton aide, c sympa
 

Reply

Marsh Posté le 13-01-2006 à 18:23:58    

un truc important: evite au maximum de construire tes requetes SQL comme tu le fait la, en inserant directement les variables!
utilise les place holders (avec les '?'), tu y gagnera en efficacité et surtout en robustesse.
 
sinon esque tu peut mettre juste al portion de ton code qui est lente? (la boucle d'apres ce que j'ai compris)
et dire combien de temps elle prend? (et combien d'iterations elle fait)

Reply

Marsh Posté le 16-01-2006 à 09:53:04    

salut...
 
oui c'est la boucle "foreach my $elm (@applications)" ds la boucle "foreach my $row (@$table)" qui prend du tps.
pr une base de 100 000 flux, je passe 49000 fois dans la boucle "foreach my $row (@$table)", pour un tps d'execution de 1200s.
je ne vois pas comment gagner la dessus...
 
merci

Reply

Marsh Posté le 16-01-2006 à 09:58:09    

attends je comprend pas, tu a toujours la boucle for(@application) ???
le but du hash (des hashs) c'etait justement de la supprimer!
 
j'ai pas lu ton code (tropg gros)
balance juste le bout utile

Reply

Marsh Posté le 16-01-2006 à 10:13:34    

oui j'ai tjs la boucle for, car je ne parcours js la boucle "foreach my $row (@$table)" integralement, je sors meme souvent au 1e item, je n'ai pas de difference avec l'ajout des hash(s)., je l'ai dc gardee.
 
juste le bout utile :  
 
foreach my $row (@$table)
{
   foreach my $elm (@applications)                    
   #for (my $k=0; $k<@applications; $k++)
   {
      if (( (@$row[3] ne '') && (@$row[3] eq $elm[0]) ) ||  
          ( (@$row[4] ne '') && (@$row[4] eq $elm[1]) ) ||
          ( (@$row[5] ne '') && (@$row[5] eq $elm[2]) ))  
          {      
            #Ce flux appartient bien a cette application
            #On ajoute donc les données de ce flux
            $ref_data=$applications[$k]->[3];
            %tmpData=%{$ref_data};
            $ident=$k;
            last;
          }
   }
}
 
merci

Reply

Marsh Posté le 16-01-2006 à 10:56:51    

ok si tu dis que ca sort toujours dans les premiers, mais la hash est tout de meme plus generique
 
Un truc que je comprens pas: tu a 100 000 flux, donc 100 000 entrée dans @$table non? Alors pourkoi seulement 49 000 iterations?
1200s ca me parait plus qu'enorme pour une petite boucle de meme pas 100 000 iteration avec seulement un petite copie de hash table dedans ! (d'ailleurs à koi te ser %tmpData ?
 
Il doit y avoir autre chose, c'est pas possible
 
sinon pour ton code je te conseil d'utiliser for à la plce de foreach (mais avec la meme syntaxe que le foreach, simplement le mot clé foreach est un peu deprecated depuis la fin de perl 4...), et surtout de construire tes requetes SQL sans inserer directement tes valeurs dedans comme tu el fait la (placeholders)

Reply

Marsh Posté le 16-01-2006 à 11:12:43    

ok avec la hash, je suis d'accord.
 
100k flux ds la table, le resultat de la requete Postgresql me donne un tableau de 49k lignes.
 
oui, je trouve que 1200s est enorme !, ma machine de test est une bi proc 450 Mhz (SPARC).
disons que je fais plein de petits traitements qui bout a bout prenne du tps.
 
tmpData me sert a concatener les donnees par la suite pr construire un histogramme temporel des flux : "$applications[$ident]->[3]=\%tmpData;"
 
for n'est pas plus long que foreeach ?
 
ok pr les requetes, le pb c'est quelles st enormes !, j'ai un peu peur de peter qcq chose....

Reply

Marsh Posté le 16-01-2006 à 11:25:11    

non for et foreach c'est la meme chose (le meme keyword)
ce qui change c'est ($=0;$i<$x;$i++) ou (@a) tu vois?
 
ok donc d'apres ce que tu me dis c'est pas tellement cette boucle la qui bouffe du temps mais l'ensemble de ta fonction. Deja une sub de plus de 500 lignes c'est pas bon signe.
je vais jetter un oil la dedans si j'ai le temps tt à l'heure

Reply

Marsh Posté le 16-01-2006 à 11:26:41    

en attendant essai de lancer ton script en DProf pour voir ce qui rame
 

Code :
  1. > perl -d:DProf script.pl
  2. ...
  3. > dprofpp


Message édité par pospos le 16-01-2006 à 11:26:57
Reply

Marsh Posté le 16-01-2006 à 11:52:54    

ce qui change c'est ($=0;$i<$x;$i++) ou (@a) tu vois?
........heuuuu non pas trop !
 
merci de ton aide, c sympa.
 
lancer dprofpp ds un scipt cgi c possible ?
c un package : use Classes::GraphSystem::GraphGenerator;
 
GraphGenerator est le nom de mon module Perl ou se trouve cette methode.

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed