Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

Sujet : lenteur d'exécution [ Divers / Aide et documentation ] (jnmchl)

samedi 23 février 2008 à 21:13:34 | lenteur d'exécution

jnmchl

Membre Club

Je fais une appli destinée à piloter des réseaux de fluides dans une usine.
L'interface principale est un scrollbox comportant un graphique qui dessine le synoptique de chaque tuyau.
Ce graphique est interactif : les couleurs que doivent prendre les différents segments donne une information sur le fluide dans le tuyau donné, et en cliquant dessus l'utilisateur peut commander des vannes et des pompes.
J'ai donc créé un composant personalisé TSegment qui gère tout ce dont j'ai besoin : 3 propriétés couleurs, des propriétés d'indice etc ...
et les évènements on click, onclickdroit ...
L'aspect graphique correspond au cahier de charge.
Le gros de l'application fonctionne, mais j'ai un problème de taille : le logiciel ne tourne pas en permanence sur les micros des utilisateurs et au démarrage de l'application elle lit un fichier de paramètres (style ini) qui contient tout le graphique : les coordonnées de chaque segment entres autres. Il y a pour l'instant près de 7000 segments existants et le temps d'initialisation dure près d'une minute ... ce qui est beaucoup trop long.
Vous pouvez chargez une partie du code où je fais une boucle qui génère 520 segments ... voyez le temps que ça met !
[ Lien ]

En étudiant le phénomène (chronomètrage des étapes) :
 - en mettant le scrollbox1.visible à false ça va beaucoup plus vite, mais ça reste trop long
 - le ralentissement est de plus en plus important vers la fin quand il y a beaucoup de segments
 - la part create et plus longue que la part formatage (procédure formate : calculs) et Paint (environ 3/4 - 1/4)

Que peut-on optimiser ?
Est-ce lié à un problème de mémoire ? de ressources ?

Merci de votre aide.

Jean-Michel


dimanche 24 février 2008 à 12:18:35 | Re : lenteur d'exécution

cantador

Membre Club
  for x := 0 to 9 do
    for y := 0 to 13 do

essaie à la place de mettre un Repeat Until...

cantador

dimanche 24 février 2008 à 13:03:48 | Re : lenteur d'exécution

Caribensila

Membre Club
Salut,

Essaie de tout dessiner dans un TBitmap en mémoire, puis d'assigner ce TBitmap à ta ScrollBox en une seule fois.

Autre idée, utiliser OpenGL qui est très rapide et qui te donnerait un synoptique 3D. Beaucoup plus "parlant" pour une usine à gaz.   :)

dimanche 24 février 2008 à 20:40:49 | Re : lenteur d'exécution

WhiteHippo

Membre Club

Bonsoir

Le principal problème est que tu utilises des composants dérivés de TWinControl. Composants dits "fenêtrés" qui sont "lourds" car à chaque composant est associé un handle pour recevoir entre autre le focus. Il aurait été préférable de dériver tes TSeg à partir de la classe TGraphicControl.

Histoire de vérifier la théorie, voici quelques modifications à apporter dans l'unité module :
  
  Dans la section interface :
    
TSegment = class(TGraphicControl) //(TCustomControl)

  Dans Formate, mise en commentaires :
    // SetWindowRgn( Handle, frmRegion, true );
    // Paint;

  Dans Paint, appel de Formate :

    Formate;

Après il reste quand même du boulot (gestion du scrollbox, visuel des lignes, etc...). Là c'est juste une modification ultra rapide pour te faire voir la différence de vitesse lorsque tu n'as pas de controles fenêtrés. Je ne serais donc pas surpris qu'il faille repenser et puis recoder une partie de ton programme.

P.S. Allez une petite piqure de rappel (http://www.delphifr.com/tutoriaux/DELPHI-DEVELOPPER-COMPOSANTS_195.aspx)

Cordialement.


"L'imagination est plus importante que le savoir." Albert Einstein


dimanche 24 février 2008 à 20:57:28 | Re : lenteur d'exécution

florenth

Membre Club
Salut ! Hum oui en effet, y'a pas mal besoin d'optimiser tout ça !!! J'ai déjà trouvé trois (voire quatre suivant comment on compte) gros gouffres à temps dans ton code : [un peu] 1- Créer et détruire ta fonte à chaque fois au lieu de la conserver [un peu plus] 2- La sur-utilisation de Regions pour donner une forme à tes composants prend un sacré paquet de temps à s'exécuter et en plus c'est pas super pratique. [énorme gouffre !] 3- Tu as troooop de composants ! C'est tout simplement ça ! Car tu aurais du créer un unique composant qui contiendrait une liste d'objets (TObjectList ou simple tableau) (voire de records) et chacun d'entre eux contiendrait les infos sur ton tuyau. Ton composant aurait alors deux rôles: - Les stocker (il en contient la liste, via le tableau) - Les dessiner (dans ce cas, une boucle for sur tout le tableau suffit à tout dessiner) Et pour éviter de se casser les yeux, on dessine tout dans un tampon bitmap comme le précise Caribensila (surtout si c'est dans un TScrollBox), tampon qui est mis à jour à chaque fois que tes infos changent (et non pas à chaque appel de Paint). Et si ça suffit pas, on met DoubleBuffered à True, ce qui nous fera un TripleBuffer en réalité, donc ça devrait largement convenir. Donc, pour répondre à ton ultime question, le problème est plutôt au niveau des ressources (sur-utilisation d'objets lourds, surtout un TCustomControl, tu doubles presque la vitesse en remplaçait juste "=class(TcustomControl)" par "=class(TGraphicControl)" Voila, c'est déjà un bon début ! A+ Flo

dimanche 24 février 2008 à 20:58:58 | Re : lenteur d'exécution

florenth

Membre Club
Ahhh, WhiteHippo, on s'est croisé ! Salut au passage ! [et Jean-michel, profites-en pour noter la remarque sur le TCustomControl ^^, comme quoi !]

dimanche 24 février 2008 à 21:55:32 | Re : lenteur d'exécution

florenth

Membre Club
Comme je suis trop gentil ^^ je t'ai fait une unité d'exemple pour que tu voies comment faire. Tout y est presque, il ne manque "que" tes procédures de calculs des coordonnées car je n'y comprenais pas grand chose. Note bien la différence avec ta version ! Et n'hésites pas à demander des précisions, j'ai commenté un max mais bon c'est jamais super explicite. Bonne lecture ! -------------------------------------------------------------------------------- unit Module; interface uses Windows, SysUtils, Classes, Graphics, Controls, Math, Contnrs; type (* Deux manières différentes de stocker tes coordonnées mais identiques en mémoire (donc transtypage possible si besoin) *) TSegmentCoords = array[0..1] of TPoint; // [0]:(X1,Y1) [1]:(X2,Y2) TSegmentCoords2 = array[0..3] of Integer; // [0]:X1 [1]:Y1 [2]:X2 [3]:Y2 (* déclaration avancée de la classe de dessin *) TSegmentDrawer = class; (* Classe représentant un segment à dessiner *) TSegment = class(TObject) private { ID unique (technique de sioux pour pouvoir l'identifier à partir de la position (X,Y) de la souris) } FID: Word; { Référence vers le propriétaire } FOwner: TSegmentDrawer; { Coordonnées (fournies par l'utilisateur) (correspond à tes FFX1, ... } FUserCoords: TSegmentCoords; { Coordonnées calculées } FCalcCoords: TSegmentCoords; // FX1, FY1, FX2, FY2 FNx: Integer; FNy: Integer; FACalc: TSegmentCoords; // A1x, A1y, A2x, A2y FBCalc: TSegmentCoords; // B1x, B1y, B2x, B2y FAMaxX: Integer; FAMaxY: Integer; FAMinX: Integer; FAMinY: Integer; FX: Integer; FCad: Extended; FCop: Extended; FAlp: Extended; { Diverses données } FA1, FA2: Boolean; FEtiq: string; FNum: string; FTpe: string; FInd: string; public constructor Create(AID: Word; AOwner: TSegmentDrawer); procedure Format; procedure PaintTo(ACanvas, AMapCanvas: TCanvas); { Propriétés } property ID: Word read FID; property Point1: TPoint read FUserCoords[0] write FUserCoords[0]; property Point2: TPoint read FUserCoords[1] write FUserCoords[1]; property A1: Boolean read FA1 write FA1; property A2: Boolean read FA2 write FA2; property NumId: string read FNum write FNum; property Tpe: string read FTpe write FTpe; property Etiq: string read FEtiq write FEtiq; property Indice: string read FInd write FInd; end; (* Procédure de l'évènement de clic *) TSegmentClickEvent = procedure (ASegment: TSegment) of object; (* Classe permettant le dessin des segments *) TSegmentDrawer = class(TGraphicControl) private { Liste de segments } FList: TObjectList; { Dernier ID affecté } FLastID: Word; { Couleurs } FCouleur: TColor; FCouleurLbl: TColor; FCouleurLigne: TColor; { Bitmap temporaire de dessin } FTempBmp: TBitmap; { Bitmap permettant de relier des coordonnées à un segment } FMapBmp: TBitmap; { Evènement de clic } FOnSegmentClick: TSegmentClickEvent; { Getters } function GetSegment(I: Integer): TSegment; function GetSegmentCount: Integer; { Setter } procedure SetCouleur(I: Integer; C: TColor); protected procedure Resize; override; procedure Paint; override; procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; { Ajoute un segment à la liste et le renvoit } function AddSegment: TSegment; { Permet aux segments de se dessiner } procedure Actualize; published property Couleur: TColor index 0 read FCouleur write SetCouleur; property CouleurLabel: TColor index 1 read FCouleurLbl write SetCouleur; property CouleurLigne: TColor index 2 read FCouleurLigne write SetCouleur; end; implementation { ******************************* TSegment ************************************* } constructor TSegment.Create(AID: Word; AOwner: TSegmentDrawer); begin inherited Create; FID := AID; FOwner := AOwner; end; procedure TSegment.Format; begin { Là tu fais tes calculs comme dans l'autre procédure mais de façon à ne PAS utiliser les Regions (mais ce sera facile vu les polygones que tu crée) } end; procedure TSegment.PaintTo(ACanvas, AMapCanvas: TCanvas); begin { Ici, tu vas dessiner ton segment en DOUBLE : 1- D'abord normalement pèour l'affichage, en dessinant dans ACanvas. Tu peux utiliser FOwner.Couleur, ... pour mettre en forme. 2- Et ensuite uniquement les contours de ton tuyau (sans le texte) que tu vas peindre avec une couleur ID et un contour ID, pour pouvoir permettre ensuite de le retrouver avec ses coordonnées lors du clic } { >> Exemple s'il s'agissait d'un rectangle (les membres utilisées sont bien sûr là juste pour l'exemple aussi) } // Dessin "normal" ACanvas.Brush.Color := FOwner.CouleurLigne; ACanvas.FrameRect(Rect(FCalcCoords[0], FCalcCoords[1])); ACanvas.TextOut(FCalcCoords[0].X, FCalcCoords[0].X, FNum); // Dessin dans la carte (map) AMapCanvas.Pen.Color := TColor(FID); // on prend la couleur "ID" AMapCanvas.Pen.Color := TColor(FID); // meme si en réalité c'est un nombre AMapCanvas.Rectangle(Rect(FCalcCoords[0], FCalcCoords[1])); // on dessine le même rectangle, mais plein cette fois et avec la couleur "ID" end; { **************************** TSegmentDrawer ********************************** } constructor TSegmentDrawer.Create(AOwner: TComponent); begin inherited Create(AOwner); {>> Création des objets } FList := TObjectList.Create(True); FTempBmp := TBitmap.Create; FMapBmp := TBitmap.Create; {>> Initialisation des propriétés } FLastID := 0; FCouleur := clBlue; FCouleurLbl := clBlue; FCouleurLigne := clBlue; end; destructor TSegmentDrawer.Destroy; begin FMapBmp.Free; FTempBmp.Free; FList.Free; inherited Destroy; end; function TSegmentDrawer.AddSegment: TSegment; begin {>> Crée un segment avec son identifiant unique (son numéro) } Inc(FLastID); Result := TSegment.Create(FLastID, Self); end; procedure TSegmentDrawer.Resize; begin inherited Resize; {>> On dimentionne nos bitmaps comme le composant } FTempBmp.Width := ClientWidth; FTempBmp.Height := ClientHeight; FMapBmp.PixelFormat := pf24Bit; FMapBmp.Width := ClientWidth; FMapBmp.Height := ClientHeight; FMapBmp.PixelFormat := pf16Bit; {>> Actualisation } Actualize; end; procedure TSegmentDrawer.Actualize; var I: Integer; begin {>> Efface tout } FTempBmp.Canvas.Brush.Color := Color; FTempBmp.Canvas.FillRect(FTempBmp.Canvas.ClipRect); FMapBmp.Canvas.Brush.Color := Color; FMapBmp.Canvas.FillRect(FMapBmp.Canvas.ClipRect); {>> Boucle pour que tous les segments se dessinent } for I := 0 to GetSegmentCount - 1 do GetSegment(I).PaintTo(FTempBmp.Canvas, FMapBmp.Canvas); end; procedure TSegmentDrawer.Paint; begin {>> Dessine tout simplement le bitmap } Canvas.Draw(0, 0, FTempBmp); end; procedure TSegmentDrawer.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var I, ID: Word; begin {>> Recherche du pixel qui nous intéresse qui va nous donner l'ID du segment cliqué } ID := Word(FMapBmp.Canvas.Pixels[X, Y]); {>> Si on tombe sur un segment (ID <> 0) alors évènement (après recherche du concerné dans la liste) } if (ID <> 0) and Assigned(FOnSegmentClick) then begin for I := 0 to GetSegmentCount - 1 do if GetSegment(I).ID = ID then begin FOnSegmentClick(GetSegment(I)); Break; end; end; end; function TSegmentDrawer.GetSegment(I: Integer): TSegment; begin Result := FList[I] as TSegment; end; function TSegmentDrawer.GetSegmentCount: Integer; begin Result := FList.Count; end; procedure TSegmentDrawer.SetCouleur(I: Integer; C: TColor); begin {>> Modifie } case I of 0: FCouleur := C; 1: FCouleurLbl := C; 2: FCouleurLigne := C; end; {>> Actualise ! } Actualize; Invalidate; end; end. -------------------------------------------------------------------------------- A bientôt j'espère ! Flo

dimanche 24 février 2008 à 22:17:52 | Re : lenteur d'exécution

Delphiprog

Administrateur CodeS-SourceS
Je rejoins l'analyse de mes éminents confrères  et j'ajoute que tu devrais t'intéresser au design pattern poids mouche (flyweight en anglais) car il correspond à la mise en oeuvre proposée par Florenth dans son troisième paragraphe (l'énorme gouffre) et exactement à ta problématique de surconsommation de composants avec leurs cortèges de méthodes.
Illustration sur le site  DoFactory.

May Delphi be with you !

Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.

lundi 25 février 2008 à 13:23:59 | Re : lenteur d'exécution

jnmchl

Membre Club
Merci pour toutes vos réponses déjà, je vais essayer de les mettre en oeuvre, je risque de vous re contacter pour ce que je n'ai pas compris ...
Toutefois, je ne sais pas si tout est applicable à l'ensemble du logiciel, car la petite démo ne représente pas l'usine (qui n'est pas à gaz ) contrairement à mon logiciel qui commence sérieusement à le devenir !
Aussi si vous pouviez me confirmer ou infirmer mes questions :

tout dessiner dans un TBitmap en mémoire :
  - oui mais : est-ce que l'interactivité fonctionne ? click + click droit ?
  - attention certains segments doivent clignoter est-ce rapide ? 
utiliser OpenGL :
  - heu oui, je vais le proposer à la maîtrise d'ouvrage ... mais il faut que je me forme d'abord à la 3D ... je crains devoir demander un délai non raisonnable
Il aurait été préférable de dériver tes TSeg à partir de la classe TGraphicControl.
  - j'avais essayé, mais je crois que je me suis cassé le nez sur :
la manière de dessiner dans le canvas, la récupération des clics et la forme à donner au logiciel (régions). Mais si ça accélère la chose je suis preneur ... mais je revoir ce qui bloquait.
 
Créer et détruire ta fonte à chaque fois au lieu de la conserver
  - On m'avait dit de toujours libérer les ressources ? En fait la police inclinée que je crée prend l'angulation du segment en fonction de x1,y1 et x2,y2, elle est donc particulière à chaque segment.

La sur-utilisation de Regions pour donner une forme à tes composants
  - Oui mais le composant doit avoir la forme du segment de tuyau : s'il y a deux tuyaux parallèles inclinés à 45° : l'enveloppe carré ne permet pas de cliquer sur le bon tuyau ... pour les test sur A1 et A2 il y a certes une part d'esthétique mais aussi la visualisation d'un séparation. 

Tu as troooop de composants ! C'est tout simplement ça ! 
  - ben pour l'instant il y a 7000 segments (et c'est pas fini ...) chaque segment doit être cliquable et avoir ses propres couleurs ... Est-ce que le fonctionnement final sera similaire avec TObjectList ou simple tableau.  
 
Autre précision de ma part :
Quand je parle de lenteur d'exécution c'est surtout au démarrage la phase d'initialisation pendant laquelle je créé les composants dans mon scrollbox. Le reste : animation : changement de couleurs, clignotement, et action de la souris fonctionne bien.  
Le fichier qui donne le schéma est issu d'un autre logiciel dont je ne suis pas l'auteur : il servait à leur précedent logiciel de commande et de contrôle, les segement peuvent avoir des coordonnées x1,x2 y1,y2 indépendant de ce que moi je voudrais.

Merci encore à tous, je vais essayer toute vos méthodes ... avant de cocher "réponse acceptée",  mais merci encore une fois. 

Jean-Michel

lundi 25 février 2008 à 14:01:27 | Re : lenteur d'exécution

florenth

Membre Club
Leurs segments sont définis par deux points : (x1,y1) et (x2,y2) Cela veut dire que c'est toi même qui leur donne leur épaisseur, qui est donc constante ? Sinon, pour répondre à tes questions : "est-ce que l'interactivité fonctionne ? click + click droit ?" => C'est vrai que ça va compliquer un peu la chose, la preuve dans mon unité d'exemple, mais ça reste faisable bien sûr. "attention certains segments doivent clignoter est-ce rapide ?" => Tu ne nous avais pas décrit ce besoin, dans ce cas il va falloir penser à distinguer deux cas : - Celui où tu as besoin de tout dessiner (une fois au début puis si jamais tu changes la taille du composant) - Celui où tu ne redessines que le segment qui clignote. Dans ce cas là, ce sera rapide. "Utiliser OpenGL" => Je te le déconseille, sauf si jamais tu as déjà des connaissances sûres. apprendre OpenGL pour ce genre de contexte me parait énorme. "sur les TGRaphiControl: j'avais essayé, mais je crois que je me suis cassé le nez" => Étrange, car si tu reprends ton code et que tu changes juste la déclaration, tout fonctionne aussi et tu gagnes (un petit peu) en vitesse. "On m'avait dit de toujours libérer les ressources" => Oui, mais rien ne t'empèches de la créer au tout début de ton programme et de la libérer à la toute fin ! ça consomme un peu de mémoire, mais c'est bien pratique ! "Oui mais le composant doit avoir la forme du segment de tuyau" => C'est justement pour cela que passer par un composant par tuyau n'est pas une super idée. Avec le design pattern que te décris Delphiprog, tu n'auras plus ce problème du tout. "Est-ce que le fonctionnement final sera similaire avec TObjectList ou simple tableau" => Le fonctionnement sera strictement identique. La seule chose qui changera, c'est l'intérieur du composant. Mais ça n'aura aucune influence sur l'aspect extérieur donc ton code précédent sera compatible. (si toutefois tu l'as codé en respectant les règles de la POO, ce qui n'est pas tout à fait le cas dans l'exemple que tu donnes en zip) "Quand je parle de lenteur d'exécution c'est surtout au démarrage la phase d'initialisation pendant laquelle je créé les composants dans mon scrollbox. Le reste : animation : changement de couleurs, clignotement, et action de la souris fonctionne bien." => C'est normal, mais tu auras toujours un temps de chargement significatif (même avec ces changements) car dessiner 7000 segments prend toujours un temps fou (sauf en OpenGL mais c'est une autre histoire). Le gain sera au niveau de la la gestion des ressources qui sera beaucoup plus légère (d'où le nom du DP, flyweight)


1 2 3

Cette discussion est classé dans : graphique, exécution, segments, lenteur, tuyau


Répondre à ce message

Sujets en rapport avec ce message

Réactualiser une fenêtre lors de l'exécution d'un processus 'long' [ par PinOff ] Je cherche à savoir comment faire le DoEvents de VB en Delphi, pour éviter que dans une boucle, mon appli soit figée et blanche.Merci !!!!Vive le dépu Savoir si une procedure est en cours d'exécution [ par Jos ] Bonjour, J'aimerais donc savoir si une procedure est en cours d'éxécution, de façon à déterminer si celle-ci a lancé l'exécution d'une autre.Merci. Pb d'exécution [ par Ethanazieff ] Salut,J'utilise Delphi 6 et j'ai de soucis pour exécuter mes appli. J'ai le msg suivant :"Exception externe C000001D"Merci d'avance pour votre aide. Graphique sur un TEdit [ par magicvinni ] Bonjour, j'ai un dessin sur la fond de ma fiche et un TEdit dessus mais invisible au debut de l'application. Quand je veux faire un rectangle sur le f Selection graphique [ par jlg75 ] Pb sur mon prog de dessin (DAO). Je souhaite acceder à l'information au niveau pixel pour selectionner l'entite auquel il appartient (cercle,ligne..et àexécution/pas à pas [ par usmok ] j'ai un prog ki gere plusieurs bddkan j'exécute l'appli, mon prog plante (pendant kil é en train de trier des infos stovker sur une des bdd)alors ke k àexécution/pas à pas [ par usmok ] j'ai un prog ki gere plusieurs bddkan j'exécute l'appli, mon prog plante (pendant kil é en train de trier des infos stovker sur une des bdd)alors ke k Aperçu graphique [ par jlg75 ] ss quelle forme est stocké l'aperçu qui apparait dans la fenetre de dialogue d'ouverture de fichier de Windows? S'agit-t-il d'un bitmap (ou autre form Commande pour obtenir le nom de sa carte graphique et son ? [ par orelien ] Bonjour,J'aimerais connaître la commande pour obtenir le type de carte graphique et son de mon pc...Merci.Orelien. Graphique [ par sergejb ] SergeJbComment écrire un texte vertical (bas vert haut, ou haut vers bas) avec les méthodes textout, sur un canvas, avec ne fonte vectorielle ou non.


Nos sponsors

Sondage...

CalendriCode

Janvier 2009
LMMJVSD
   1234
567891011
12131415161718
19202122232425
262728293031 

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel BAÏSE, Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,546 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.