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

Lazarus Pascal Discussion :

Animation pour patienter [Lazarus]


Sujet :

Lazarus Pascal

  1. #1
    Candidat au Club
    Homme Profil pro
    Enseignant
    Inscrit en
    mai 2018
    Messages
    18
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : mai 2018
    Messages : 18
    Points : 2
    Points
    2
    Par défaut Animation pour patienter
    J'ai essayé d'insérer une animation pour faire patienter le temps d'un calcul.
    L'animation, à la fréquence gérée par un composant Ttimer se déclenche bien si j'utilise par exemple un Tbutton "Flash" et s'arrête avec un "stop".
    Mais rien ne se passe si je la déclenche en début de boucle et l'arrête en fin de boucle.
    Dans l'exemple je calcule une valeur de pi/4 par la série de Leibnitz : avec 100000000 de termes, ma bécane met 18 secondes (elle est vieille, soyez indulgents), donc une animation pour patienter serait la bienvenue...
    Tous les conseils seront examinés attentivement, merci d'avance
    Voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      inc(start);             //chaque Timer1.interval (ici 1/2 seconde)
      n:=(start mod 2);       //donne la parité de start
      case n of               //alterne les couleurs selon pair ou impair
      0: Shape1.Brush.Color:=clblue;
      1: Shape1.Brush.Color:=clfuchsia;
      end;
    end;
     
    procedure TForm1.chrono(t: word);
    var h,m,s,ms,st : word;
    begin
       decodetime(time,h,m,s,ms);
       case t of
       0: t0:=h*3600+m*60+s;          //heure début du calcul
       1: begin t1:=h*3600+m*60+s;    //heure de fin du calcul
          st:=t1-t0;
          temps.caption:='Durée '+inttostr(st)+' s';
          end;
       end
    end;
     
    procedure TForm1.leibnitz;
    //calcul de Pi/4 par le Développement Limité de ArcTan(1)
    var i,t:longword;
      k:integer;
      w:real;
    begin
      t:=1000000000;  //nombre de termes de la somme
      chrono(0);    //top chrono
    //  start:=0;timer1.enabled:=true;
      w:=1; k:=-1;
      for i:=1 to t do
        begin
         w:=w+k/(2*i+1);
         k:=-k ;
        end;
       labelLeibnitz.caption:='Leibnitz donne Pi/4= '+floattostr(w);
       chrono(1); //Stop chrono
       labelnbdetermes.caption:='avec '+inttostr(t)+' termes';
    //   timer1.enabled:=false;
    end;
     
    procedure TForm1.Button1Click(Sender: TObject); //flash
    begin
      start := 0;
      Timer1.Enabled := True;
    end;
     
    procedure TForm1.Button3Click(Sender: TObject); //stop flash
    begin
      Timer1.Enabled := False;
    end;

  2. #2
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    mars 2005
    Messages
    3 789
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : mars 2005
    Messages : 3 789
    Points : 11 107
    Points
    11 107
    Billets dans le blog
    6
    Par défaut
    Bonjour,
    La boucle de calcul ne rend pas la main au système pour lui permettre d'exécuter les affichages.
    Une solution pourrait être de tout inclure dans la procédure de calcul, de la façon suivante (approximative, non testée) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    function TForm1.leibnitz(NbTermes: Longword): Real;
    //calcul de Pi/4 par le Développement Limité de ArcTan(1)
    const
      Intervalle = 100; // pour rafraîchir toutes les 100 ms
    var 
      i: longword;
      k: integer;
      w: real;
      d,f: TDateTime;
    begin
      d := Now; // heure de départ
      w:=1;
      k:=-1;
      for i:=1 to NbTermes do
      begin
        w:=w+k/(2*i+1);
        k:=-k ;
        if NbTermes mod Intervalle = 0 then // pour ne pas appeler l'heure trop souvent
        begin
          f := Now;
          if f >= d + Intervalle/MilliSecondsPerDay then
          begin
            d := d + Intervalle/MilliSecondsPerDay;
            if Shape1.Brush.Color = clblue then
              Shape1.Brush.Color := clfuchsia
            else
              Shape1.Brush.Color := clblue;
     
              // probablement faut-il demander un FillColor et un Repaint de Shape1 ?
     
          end;
        end;
      end;
      Result := w;
    end;
    end;
     
    procedure TForm1.Button1Click(Sender: TObject); 
    var
      debut: TDateTime;
      t: LongWord;
    begin
      Debut := Now;
      t:=1000000000;  //nombre de termes de la somme
      labelLeibnitz.caption:='Leibnitz donne Pi/4= '+floattostr(Leibnitz(t));
      labelnbdetermes.caption:='avec '+inttostr(t)+' termes';
      labelTemps.Caption := 'en ' + Millisecondsbetween(Now, debut).ToString + ' millisecondes'; 
    end;
    Delphi 5 Pro - Delphi 10.4 Rio Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  3. #3
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    septembre 2008
    Messages
    5 564
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 564
    Points : 12 779
    Points
    12 779
    Par défaut
    Au moins sous Windows (mais est-ce fondamentalement différent sous Linux ?), autant le timer que l'affichage sont impactés puisque le timer réagit à un message WM_TIMER, la repeinture à WM_PAINT et que la boucle empêche le traitement de la pile de messages.

    C'est typiquement le genre de tâche à déporter dans un thread de travail, on synchronise juste la récupération du résultat.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    type
     
      { TPi4Thread }
     
      TPi4Thread = class(TThread)
      protected
        procedure Execute; override;
        procedure OnTerminate;
      public
        Loops  :LongWord;
        Chrono :QWord;
        Result :double;
      end;
     
      { TForm1 }
     
      TForm1 = class(TForm)
        Button1: TButton;
        labelLeibnitz: TLabel;
        labelnbdetermes: TLabel;
        LabelDuree: TLabel;
        Shape1: TShape;
        Timer1: TTimer;
        procedure Button1Click(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
      private
        FlipFlop :boolean;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.lfm}
     
    { TPi4Thread }
     
    //calcul de Pi/4 par le Développement Limité de ArcTan(1)
    procedure TPi4Thread.Execute;
    var
      i,t :longword;
      k   :integer;
    begin
      FreeOnTerminate := TRUE;
     
      Loops  := 1000000000;  //nombre de termes de la somme
      Result := 1;
      k      :=-1;
     
      Chrono := GetTickCount64;
     
      for i:=1 to Loops do
      begin
        Result:=Result+k/(2*i+1);
        k:=-k ;
      end;
     
      Chrono := GetTickCount64 -Chrono;
      Synchronize(@OnTerminate);
    end;
     
    procedure TPi4Thread.OnTerminate;
    begin
      Form1.labelLeibnitz.caption   := Format('Leibnitz donne Pi/4 = %.5f', [Result]);
      Form1.labelnbdetermes.caption := Format('avec %d termes', [Loops]);
      Form1.labelDuree.caption      := Format('durée %f s', [Chrono /1000]);
     
      Form1.Button1.Enabled := TRUE;
      Form1.Timer1.Enabled  := FALSE;
    end;
     
    { TForm1 }
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Button1.Enabled := FALSE;
      Timer1.Enabled  := TRUE;
     
      TPi4Thread.Create(FALSE);
    end;
     
    procedure TForm1.Timer1Timer(Sender: TObject);
    const
      Progress :array[boolean] of TColor = (clBlue, clfuchsia);
    begin
      FlipFlop := not FlipFlop;
      Shape1.Brush.Color := Progress[FlipFlop];
    end;

  4. #4
    Membre expert

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

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

    Informations forums :
    Inscription : mai 2013
    Messages : 1 131
    Points : 3 558
    Points
    3 558
    Par défaut
    Bonjour,

    Il y a un ordre qui redonne momentanément la main au traitement des messages, c'est Application.ProcessMessages. C'est non seulement utile pour un éventuel timer mais aussi pour garder une certaine réactivité de l'application (réponse aux boutons par exemple).

    Mais ici, je ne suis pas sûr que j'opterais pour un Timer. Avec un simple compteur "j" incrémenté dans le corps de la boucle principale et remis à 0 tous les 10_000_000 de pas provoquant alors l'inversion de couleur, nous obtenons à moindre coût une information de progression.

    Je ne suis pas sûr que l'inversion de k soit la meilleure approche. Si le compilateur ne s'aperçoit pas que k est 1 ou -1, il va faire une division flottante classique c'est à dire une multiplication de k par 1/x.
    On peut espérer éviter la multiplication flottante, donc gagner un peu de temps, en écrivant un truc du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
       w := 1.0;
       i := 1;
       repeat 
          i += 2;
          w := w - 1 / i;
          i += 2;
          w := w + 1 / i;
       until i >= 2000000001;
    Je crois que le nombre de termes est à augmenter de 1 (w non nul initialement).

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

  5. #5
    Membre averti
    Homme Profil pro
    Retraité
    Inscrit en
    avril 2012
    Messages
    160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Vaucluse (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : avril 2012
    Messages : 160
    Points : 363
    Points
    363
    Par défaut
    Bonjour,

    TFPTimer ne serait-il pas adapté à ce genre de situation ? En tout cas c'est celui que j'utilise quand le TTimer est bloqué par le processus en cours...

    bb

  6. #6
    Candidat au Club
    Homme Profil pro
    Enseignant
    Inscrit en
    mai 2018
    Messages
    18
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : mai 2018
    Messages : 18
    Points : 2
    Points
    2
    Par défaut
    Citation Envoyé par tourlourou Voir le message
    Bonjour,
    La boucle de calcul ne rend pas la main au système pour lui permettre d'exécuter les affichages.
    Une solution pourrait être de tout inclure dans la procédure de calcul, de la façon suivante (approximative, non testée) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    function TForm1.leibnitz(NbTermes: Longword): Real;
    //calcul de Pi/4 par le Développement Limité de ArcTan(1)
    const
      Intervalle = 100; // pour rafraîchir toutes les 100 ms
    var 
      i: longword;
      k: integer;
      w: real;
      d,f: TDateTime;
    begin
      d := Now; // heure de départ
      w:=1;
      k:=-1;
      for i:=1 to NbTermes do
      begin
        w:=w+k/(2*i+1);
        k:=-k ;
        if NbTermes mod Intervalle = 0 then // pour ne pas appeler l'heure trop souvent
        begin
          f := Now;
          if f >= d + Intervalle/MilliSecondsPerDay then
          begin
            d := d + Intervalle/MilliSecondsPerDay;
            if Shape1.Brush.Color = clblue then
              Shape1.Brush.Color := clfuchsia
            else
              Shape1.Brush.Color := clblue;
     
              // probablement faut-il demander un FillColor et un Repaint de Shape1 ?
     
          end;
        end;
      end;
      Result := w;
    end;
    end;
     
    procedure TForm1.Button1Click(Sender: TObject); 
    var
      debut: TDateTime;
      t: LongWord;
    begin
      Debut := Now;
      t:=1000000000;  //nombre de termes de la somme
      labelLeibnitz.caption:='Leibnitz donne Pi/4= '+floattostr(Leibnitz(t));
      labelnbdetermes.caption:='avec '+inttostr(t)+' termes';
      labelTemps.Caption := 'en ' + Millisecondsbetween(Now, debut).ToString + ' millisecondes'; 
    end;
    Bonjour, j'avais déjà envisagé d'inclure un affichage dans la boucle : tous les 1000 pas, afficher combien il reste de pas avant la fin de la boucle. Mais rien n'est affiché tant que la boucle n'est pas achevée... C'est pourquoi je me suis tourné vers l'option Timer. Merci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    for i:=1 to t do
        begin
         w:=w+k/(2*i+1);
         k:=-k ;
         if (i mod 1000)=0 then
           begin
           labeldecompte.caption:=inttostr(t-i);
           end;
        end;

  7. #7
    Candidat au Club
    Homme Profil pro
    Enseignant
    Inscrit en
    mai 2018
    Messages
    18
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : mai 2018
    Messages : 18
    Points : 2
    Points
    2
    Par défaut
    Citation Envoyé par Guesset Voir le message
    Bonjour,

    Il y a un ordre qui redonne momentanément la main au traitement des messages, c'est Application.ProcessMessages. C'est non seulement utile pour un éventuel timer mais aussi pour garder une certaine réactivité de l'application (réponse aux boutons par exemple).

    Mais ici, je ne suis pas sûr que j'opterais pour un Timer. Avec un simple compteur "j" incrémenté dans le corps de la boucle principale et remis à 0 tous les 10_000_000 de pas provoquant alors l'inversion de couleur, nous obtenons à moindre coût une information de progression.

    Je ne suis pas sûr que l'inversion de k soit la meilleure approche. Si le compilateur ne s'aperçoit pas que k est 1 ou -1, il va faire une division flottante classique c'est à dire une multiplication de k par 1/x.
    On peut espérer éviter la multiplication flottante, donc gagner un peu de temps, en écrivant un truc du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
       w := 1.0;
       i := 1;
       repeat 
          i += 2;
          w := w - 1 / i;
          i += 2;
          w := w + 1 / i;
       until i >= 2000000001;
    Je crois que le nombre de termes est à augmenter de 1 (w non nul initialement).

    Salutations
    Merci pour la suggestion.
    Je viens de tester avec affichage tous les 1000 pas de la boucle le nombre de pas restant.
    C'est bon, même si ça va très vite pour cette boucle de calcul, l'affichage du nombre de pas restant défile bien.
    Merci beaucoup, je vais tenter de faire "clignoter" un Tshape, Et puis avec une autre animation contrôlée par un Timer.

    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
    procedure TForm1.GoClick(Sender: TObject);
      var i:longword;  //calcul de Pi/4 par le Développement Limité de ArcTan(1)
      k:integer;
      w:real;
    begin
    //  chrono(0); timer1.enabled:=true;//top chrono
      labeldecompte.caption:=inttostr(t);
      w:=1; k:=-1;
      for i:=1 to t do
        begin
         w:=w+k/(2*i+1);
         k:=-k ;
         if (i mod 1000)=0 then
           begin application.processmessages;
           labeldecompte.caption:=inttostr(t-i);
           end;
        end;
       labeLLeibnitz.caption:='Leibnitz donne Pi/4= '+floattostr(w);
     
    //   chrono(1);timer1.enabled:=false; //Stop chrono
    end;
    Bravo à tous ceux qui ont pris le temps de cogiter la question et de proposer une réponse.

  8. #8
    Candidat au Club
    Homme Profil pro
    Enseignant
    Inscrit en
    mai 2018
    Messages
    18
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : mai 2018
    Messages : 18
    Points : 2
    Points
    2
    Par défaut avec couleur clignotante
    ça fonctionne aussi avec une alternance de couleur d'une Tshape:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    if (i mod 1000)=0 then
           begin 
             application.processmessages;
             labeldecompte.caption:=inttostr(t-i);
             with Shape1.Brush do
                 if color=clwhite then color:=clblack else color:=clwhite;
           end;
    Mais il faut mettre des lunettes de soleil, sinon l'ellipse clignotante risque de vous tourner épileptique !

  9. #9
    Membre expert

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

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

    Informations forums :
    Inscription : mai 2013
    Messages : 1 131
    Points : 3 558
    Points
    3 558
    Par défaut
    Bonjour,

    S'il met 18 secondes pour traiter le milliard, en interrompant 180 fois le processus (arrondi à 200) l'actualisation de l'affichage se fera au 1/10 s ce qui est très suffisant pour l'être humain. soit toutes les 5 millions d'occurrences. Allez plus vite ne sert à rien car le système n'a alors pas le temps de traiter toutes les actualisations.

    Par ailleurs, il est préférable d'éviter mod qui suppose une division ce qui consomme pas mal de temps alors que nous sommes dans un simple incrément. En ajoutant un simple j : int32 initialisé à 5 millions et décrémenté à chaque boucle.

    Il est préférable de placer le application.processmessages: après les modifications car c'est lui qui va en permettre l'affichage (la différence est plus intellectuelle qu'autre chose car nous sommes bien en peine de nous apercevoir que l'affichage a 1/10 s de retard).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       j := 5000000;                                 // Avant la boucle
       ...
       dec(j);
       if j <= 0 then begin 
             j := 5000000; 
             labeldecompte.caption := inttostr(t-i);
             with Shape1.Brush do
                if color = clwhite then color := clblack else color := clwhite;
             Application.ProcessMessages;
           end;
       ...
    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  10. #10
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    décembre 2011
    Messages
    4 016
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : décembre 2011
    Messages : 4 016
    Points : 15 091
    Points
    15 091
    Billets dans le blog
    8
    Par défaut
    Si je peux me permettre, Application.ProcessMessages; peut faire l'affaire mais c'est du bricolage. La solution proposée par Andnotor est plus propre à mon humble avis.

  11. #11
    Membre expert

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

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

    Informations forums :
    Inscription : mai 2013
    Messages : 1 131
    Points : 3 558
    Points
    3 558
    Par défaut
    Bonjour,

    La solution de AndNotOr est de toute évidence la plus élégante.

    Cependant, elle ne donne pas d'information de progression, ce que fait la solution fruste (sans même un timer) avec un code très court.

    Il y a toujours 1000 façons de tondre un œuf .

    On pourrait prendre un sujet quelconque et fixer divers objectifs :
    • Avoir le code le plus rapide possible
    • Avoir le code le plus lent possible sans code mort ni artifice comme sleep.
    • Avoir le code qui donne l'exécutable le grand possible sans code mort
    • Avoir le code qui donne l'exécutable le petit possible
    • Avoir le code le plus court possible
    • Avoir le code le plus consommateur de mémoire sans consommation extra traitement
    • Avoir le code le plus économique en mémoire
    • etc.


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

  12. #12
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    mars 2005
    Messages
    3 789
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : mars 2005
    Messages : 3 789
    Points : 11 107
    Points
    11 107
    Billets dans le blog
    6
    Par défaut
    Un code que l'on sait d'exécution longue peut être prévu pour déclencher régulièrement une CallBack, par exemple, ou exécuté dans un thread, comme l'a proposé Andnotor.

    On peut ajouter comme argument de comparaison le niveau de séparation entre interface et code métier : un calcul (lent) a-t-il à connaître de l'IHM ? Si on la change, le code du calcul reste pourtant le même et il faudra penser à retoucher à la procédure.

    Le niveau de séparation n'a évidemment pas la même acuité dans une grosse appli, un contexte de tests unitaires, ou un programme simple.
    Delphi 5 Pro - Delphi 10.4 Rio Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  13. #13
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    septembre 2008
    Messages
    5 564
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 564
    Points : 12 779
    Points
    12 779
    Par défaut
    Disons que la demande était juste de faire clignoter un élément, pas d'avoir un retour d'avancement (mais ce serait juste ajouter TThread.Queue).

    Sinon l'avantage de travailler sur une base de temps est d'avoir un comportement identique quelque soit le système sur lequel tourne l'application (chez moi ce calcul prend moins de 4 s). Un clignotement frénétique serait plus énervant qu'autre chose

    Dans mon exemple ce qui rebute est le passage par une classe TThread mais il semble que les procédures anonymes arrivent sur Free Pascal (je vous laisserai tester). Là le code serait presque aussi compact que l'original.

    Un exemple Delphi (avec aussi l'utilisation de variables en ligne) :
    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
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Button1.Enabled := FALSE;
      Timer1.Enabled  := TRUE;
     
      //calcul de Pi/4 par le Développement Limité de ArcTan(1)
      TThread.CreateAnonymousThread(procedure
                                    begin
                                      const Loops  = 1000000000;
                                      var Result  := 1.0;
                                      var k       :=-1;
                                      var Chrono  := GetTickCount;
     
                                      for var i := 1 to Loops do
                                      begin
                                        Result := Result +k /(2 *i +1);
                                        k := -k;
                                      end;
     
                                      TThread.Synchronize(nil, procedure
                                                               begin
                                                                 labelLeibnitz.caption   := Format('Leibnitz donne Pi/4 = %.5f', [Result]);
                                                                 labelnbdetermes.caption := Format('avec %d termes', [Loops]);
                                                                 labelDuree.caption      := Format('durée %f s', [(GetTickCount -Chrono) /1000]);
     
                                                                 Button1.Enabled := TRUE;
                                                                 Timer1.Enabled  := FALSE;
                                                               end);
                                    end).Start;
    end;

  14. #14
    Membre expert

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

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

    Informations forums :
    Inscription : mai 2013
    Messages : 1 131
    Points : 3 558
    Points
    3 558
    Par défaut Précision vs exactitude
    Bonjour,

    Quand on regarde l'algorithme on a l'impression que le double avec ses 52 bits de mantisse (+1 implicite) est largement suffisant pour les 2 milliards (2*i+1) qui se contente de 31 bits.

    Mais c'est une série alternée qui ne progresse plus si la somme de 2 termes consécutifs est considérée comme nulle. Il y a le piège de la représentation flottante qui pourrait laisser penser que la précision adaptative (via l'exposant) permet de s'abstraire de cela. Cependant comme le résultat est toujours relativement proche de 1, l'addition fait disparaître nombre de décimales en obligeant un réalignement des exposants. On trouve assez facilement que i max est de l'ordre de 226 soit 67 108 864 ce qui est très loin du milliard.

    D'où l'idée de modifier le code pour qu'il s'arrête quand la valeur resultat ne progresse plus. Le code suivant est écrit pour donner au compilateur toutes les chances de bien optimiser (et il y arrive en divisant par plus de 2 les temps sans optimisation).
    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
    procedure TForm1.btnRunClick(Sender: TObject);
    var
        r, ro  : double;
        T  : UInt64;
        i  : Int32;
    begin
       btnRun.Enabled:= False;
       r  := 1.0;
       i  := 1;
       T  := GetTickCount64;
       repeat
          ro := r;
          i += 2;
          r := r - 1 / i;
          i += 2;
          r := r + 1 / i;
       until ro = r;
          stResult.Caption:= Format('PI/4 = %.9f '+#10#13+'en %d étapes'+#10#13+'et %d ms',
             [r, 1 + i >> 1, GetTickCount64 - T]);
       btnRun.Enabled:= True;
    end;
    Ca donne ce résultat qui n'a plus guère besoin de suivi.
    Nom : PI sur 4.png
Affichages : 53
Taille : 7,2 Ko
    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  15. #15
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    septembre 2008
    Messages
    5 564
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 564
    Points : 12 779
    Points
    12 779
    Par défaut
    Joli

    Juste ton calcul d'étapes est faux : i est initialisé à 1 et incrémenté de 4, ce serait (i-1) >> 2 ou plus simplement i >> 2 puisqu'un décalage de bit à droite équivaut à div.

  16. #16
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    septembre 2008
    Messages
    5 564
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 564
    Points : 12 779
    Points
    12 779
    Par défaut
    Une autre optimisation sur la base de ton code

    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
    uses Math;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
        r, ro :UInt64;
        T     :UInt64;
        i     :Int32;
        Mul   :UInt64;
    begin
       Mul := 10**12;
       r   := Mul;
       i   := 1;
       T   := GetTickCount64;
     
       repeat
          ro := r;
          i += 2;
          r := r - Mul div i;
          i += 2;
          r := r + Mul div i;
       until ro = r;
     
       stResult.Caption:= Format('PI/4 = %.9f '+#10#13+'en %d étapes'+#10#13+'et %d ms', [r /Mul, i >> 2, GetTickCount64 - T]);
    end;

  17. #17
    Membre expert

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

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

    Informations forums :
    Inscription : mai 2013
    Messages : 1 131
    Points : 3 558
    Points
    3 558
    Par défaut
    Bonjour Andnotor,

    Citation Envoyé par Andnotor Voir le message
    ...Juste ton calcul d'étapes est faux : i est initialisé à 1 et incrémenté de 4, ce serait (i-1) >> 2 ou plus simplement i >> 2 puisqu'un décalage de bit à droite équivaut à div.
    En nombre de boucles tu as raison, mais dans une boucle j'ai deux étapes (r- puis r+) car j'ai gardé le calcul originel.

    C'est du reste une faiblesse résiduelle de l'algorithme. Au moment de bravoure (quand on attaque les derniers bits utiles) nous avons deux arrondis soit une erreur moyenne d'un bit alors que si nous avions la fusion des deux termes nous aurions un seul arrondi soit une erreur moyenne d'un demi-bit.

    Cerise sur le gâteau les performances sont encore meilleures (entre 45 et 62 ms). C'est presqu'immoral

    La diminution des erreurs d'arrondis permet de gagner en précision alors que nous diminuons le nombre de calculs flottants. J'ai gardé le même décompte pour comparaison alors qu'en toute rigueur la fusion de 2 étapes les divise par 2.

    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
    procedure TForm1.btnRunClick(Sender: TObject);
    var
        T  : UInt64;
        r, ro : double;
        m, ii : Int64;
    begin
       btnRun.Enabled:= False;
       r := 1.0;
       m := 4;
       ii:= 15;
       T  := GetTickCount64;
       repeat
          ro := r;
          m  += 4;
          r  := r - 2 / ii;
          ii += (m-2) << 3;                          // (2*i)² - 1
       until  ro = r; ;
       stResult.Caption:= Format('PI/4 = %.9f '+#10#13+'en %d étapes'+#10#13+'et %d ms',
          [r, m >> 1 - 1, GetTickCount64 - T]);
       btnRun.Enabled:= True;
    end;
    Une démarche qui pourrait faire gagner en précision mais pas en temps serait de partir à l'envers, des termes les plus petits vers les premiers, (bien sûr sans l'initialisation à 1.0 de r ). Les arrondis auraient lieu après consolidation entre nombre de même ordre de grandeur.

    Je suis sûr qu'il y a encore un potentiel d'amélioration.

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

  18. #18
    Membre expert

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

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

    Informations forums :
    Inscription : mai 2013
    Messages : 1 131
    Points : 3 558
    Points
    3 558
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Une autre optimisation...
    Les entiers sont toujours préférables, mais pourquoi se contenter de 40 bits alors qu'il y en a 64 à disposition (la précison sera même meilleure que le calcul flottant). Et je ne parle pas des performances.

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

  19. #19
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    septembre 2008
    Messages
    5 564
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 564
    Points : 12 779
    Points
    12 779
    Par défaut
    Citation Envoyé par Guesset Voir le message
    En nombre de boucles tu as raison, mais dans une boucle j'ai deux étapes (r- puis r+) car j'ai gardé le calcul originel.
    Au temps pour moi

    Citation Envoyé par Guesset Voir le message
    pourquoi se contenter de 40 bits alors qu'il y en a 64
    C'est un bon compromis entre résolution et temps d'exécution. La dégradation des performances se fait sentir au-delà de 1012.

    2^40 - 724 000 boucles - 125 ms
    2^50 - 23 000 000 boucles - 3.8 s
    2^60 - 759 000 000 boucles - 2.1 min

  20. #20
    Candidat au Club
    Homme Profil pro
    Enseignant
    Inscrit en
    mai 2018
    Messages
    18
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : mai 2018
    Messages : 18
    Points : 2
    Points
    2
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Au temps pour moi



    C'est un bon compromis entre résolution et temps d'exécution. La dégradation des performances se fait sentir au-delà de 1012.

    2^40 - 724 000 boucles - 125 ms
    2^50 - 23 000 000 boucles - 3.8 s
    2^60 - 759 000 000 boucles - 2.1 min
    Merci à tous,
    les animations ralentissent les boucles de calculs, mais parfois on aime bien "pressentir" la progression...
    J'avais mis une cible sur laquelle venaient des pixels représentant des valeurs de calculs, et voulais "voir" la cible se "criblér" au fur et à mesure ds calculs.. Gràce à vous tous, c'est fait.
    Mais la précision des calculs et l'efficacité que vous avez évoquées m'ont passionné.
    Encore mille mercis.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Deux animations pour patienter
    Par Roland Chastain dans le forum Téléchargez
    Réponses: 13
    Dernier message: 01/09/2016, 09h21
  2. Animation pour patienter
    Par Paul TOTH dans le forum Contribuez
    Réponses: 10
    Dernier message: 09/11/2015, 17h37
  3. [WD16] Animation pour faire patienter l'utilisateur
    Par bombseb dans le forum WinDev
    Réponses: 8
    Dernier message: 14/02/2012, 15h19
  4. Enregistrer une animation pour le web
    Par paradeofphp dans le forum Flash
    Réponses: 1
    Dernier message: 01/08/2006, 14h30
  5. [C#] CF - Affichage du camembert pour patienter
    Par freddyboy dans le forum Windows Forms
    Réponses: 5
    Dernier message: 11/06/2004, 16h11

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