IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Problème de lag, comment détourner le problème ?


Sujet :

Langage Java

  1. #1
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2007
    Messages : 128
    Par défaut Problème de lag, comment détourner le problème ?
    Bonjour ou bonsoir,

    J'ai un soucis, expliqué ci-dessous.

    Ingrédients :
    • Une JFrame(jF) dans laquelle se trouve un JLabel(jL)
    • Un String(texte) pouvant varier et de taille(tailleTexte) pouvant également varier
    • Un Thread(defil), de priorité maximale, qui fait défiler le texte dans le jL à l'aide d'un compteur(compteur) qui est incrémenté "tous les 100 millisecondes"(je suis obligé d'avoir une périodicité d'environ 100 millisecondes)

    Code du Thread : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    			Thread defil = new Thread() {
    				public void run() {
    					compteur = 0;
    					tailleTexte = texte.length();
    					while (true) {
    						Thread.sleep(100);
    			        		tailleTexte = texte.length();
    			        		compteur = compteur % tailleTexte;
    						jL.setText(texte.substring(compteur, tailleTexte - 1) + texte.substring(0, compteur));
    			        		compteur = (compteur + 1) % tailleTexte;
    					}
    				}
    			 };

    Mon soucis est le suivant :
    J'ai un lag d'affichage.

    En dessous de 200 millisecondes, un lag se fait sentir lors de l'affichage.
    "Normal, c'est dépendant du processeur sur lequel est lancé le programme", vous allez me dire.

    MAIS j'aimerais tout de même avoir une solution.

    J'ai eu un début de solution, à savoir :
    La méthode setText pour un JLabel prend trop de temps. Donc il faut trouver un component dont le setText est plus rapide(au moins deux fois) que celui pour un JLabel, ou alors faire un gif animé du texte défilent et l'affiché.
    NB : Je n'aime pas la deuxième proposition, non pas par flemme, mais pour raison technique.

    Pouvez vous m'aider ?

    Merci


    Edit : Pour les curieux, je n'ai pas oublié de synchroniser le String texte et je n'ai pas oublié de catcher l'éventuel InterruptedException du sleep. A part ces deux "non ajouts" au code plus haut, tout est à l'identique.

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 586
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 586
    Par défaut
    Hum. J'ai essayé de faire en sorte que le changement de texte soit le plus rapide possible (précalculer en mémoire toutes les images que la label doit faire en boucle, et les transférer directement au Raster,) et le même lag est toujours là. Un .gif animé ne ferait pas mieux.

    Pour respecter une telle vitesse, deux possibilités :
    - Changer l'affichage en fonction de ce qu'il devrait être au moment où on l'affiche. C'est comme ça qu'on fait de l'affichage vidéo en principe. Le rafraîchissement n'est pas plus rapide, mais les lags se voient peu.

    - Ne pas le faire en AWT ou Swing. Le faire en Java2D (ce qui inclut le premier point au-dessus.)
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2007
    Messages : 128
    Par défaut
    Salut !

    J'en suis venu à peu près à la même conclusion, d'abord par rapport aux gif :
    Mise en mémoire des trames du gif et affichage périodique des trames. Mieux que l'affichage du texte dans un JLabel, mais toujours avec du lag.
    J'ai améliorer ça avec une petite modification, qui reprend ta remarque plus bas, plutôt que de faire digérer l'affichage des trames par le processeur en lui envoyant des instructions toutes les 100 millisecondes, je récupère l'heure toutes les 10 millisecondes et affiche les trames quand elles devraient apparaître. Le lag se fait largement moins sentir, mais est toujours présent.
    J'ai encore améliorer l'affaire.. Plutôt que d'afficher les trames les unes après les autres, j'ai assembler les trames bouts à bouts et je fais simplement glisser l'image avec la méthode cité plus haut.
    J'ai un résultat satisfaisant, mais .. j'ai l'impression de voir un lag.

    Puis par rapport à Java2D, j'ai jamais encore touché à ça, j'y toucherais plus tard, mais dans l'absolu je dois faire avec ce que j'ai pour l'instant.

  4. #4
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    Déjà, n'appelez aucune méthode de vos composants graphiques depuis un autre thread que l'EDT (voir la FAQ à ce sujet).
    Ensuite, en ce qui me concerne, je ne remarque aucun lag (hormis le fait que votre algorithme fait qu'une des combinaisons apparaît systématiquement deux fois et pourrait donner l'impression d'un arrêt sur image)

    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
    package test;
    import javax.swing.*;
     
     
    public class Test {
       static int compteur;
       public static void main(String[] argv){
          final String texte = "abcdefghijklmnop";
          JFrame frame = new JFrame();
          final JLabel test = new JLabel("test");
          frame.setSize(200,200);
          frame.getContentPane().add(test);
          frame.setVisible(true);
          Thread defil = new Thread() {
    	public void run() {
    		compteur = 0;
    			while (true) {
    				try{
    					Thread.sleep(10);
    		        		final int tailleTexte = texte.length();
    		        		compteur = compteur % tailleTexte;
    					SwingUtilities.invokeAndWait(new Runnable(){ 
    						public void run() {
    							test.setText(texte.substring(compteur, tailleTexte - 1) + texte.substring(0, compteur)); 
    						}});
    		        		compteur = (compteur + 1) % tailleTexte;
    				} catch (InterruptedException e){
    					return;
    				} catch (java.lang.reflect.InvocationTargetException e){
    					e.printStackTrace();
    				}
    			}
    	}
           };
           defil.start();
       }
    }
    edit: par contre il n'y a aucun raison de faire de la synchronisation sur votre string, et ça peut être la source de vos lags qui du code "lent" se synchronize aussi inutilement sur cette String.

  5. #5
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2007
    Messages : 128
    Par défaut
    Bonsoir

    Je tiens à préciser deux petites choses que je n'ai pas précisé au premier post parce que je ne pensais pas qu'on allait regarder de ce côté là :
    • Je ne fais pas exactement un JLabel.setText.
      J'ai plusieurs classes : Boite(extend JFrame), Panneau(extend JPanel), Bouton(extend JButton), etc
      Dans mon thread defil, je fais ceci :
      ((Boite)fenetrePrincipale).setTexte(DefilLabel, "texte qui défile");
      Donc je ne pense pas avoir de soucis avec l'EDT, j'ai toujours eu cette habitude de programmation. Mais je vais aller vérifier quand même au cas où.
    • La synchronisation que j'effectue est nécessaire. Le texte à défiler peut varier, comme préciser sur mon premier post. Un autres Thread s'occupe de récupérer ce texte à défiler et mon Thread Defil doit vérifier cette variable commune afin de l'afficher. Je dois donc synchroniser l'envoi et la consultation de cette chaîne commune.


    Je n'ai pas bien saisi ce que vous entendez par :
    je ne remarque aucun lag (hormis le fait que votre algorithme fait qu'une des combinaisons apparaît systématiquement deux fois et pourrait donner l'impression d'un arrêt sur image)
    Aucune combinaison n’apparaît systématiquement deux fois :
    (pour une chaine de n(>1 caractères))
    compteur = 0
    texte.substring(0, n - 1) + texte.substring(0, 0)
    compteur = (0 + 1) % n = 1
    texte.substring(1, n - 1) + texte.substring(0, 1)
    ..
    compteur = ((n - 2) + 1) % n = n - 1
    texte.substring(n - 1, n - 1) + texte.substring(0, n - 1)
    compteur = ((n - 1) + 1) % n = 0
    texte.substring(0, n - 1) + texte.substring(0, 0)

    Le lag dont je vous parle intervient de façon aléatoire avec un sleep de 100 millisecondes.


    Edit : Je viens de tester votre code. A 100 millisecondes, je vois bien du lag.

    Edit 2 : A l'aide de isEventDispatchThread(), je suis toujours dans l'EDT.
    Avec ou sans synchronisation(String constant), j'ai du lag.

  6. #6
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    Citation Envoyé par zizoufoot Voir le message
    Dans mon thread defil, je fais ceci :
    ((Boite)fenetrePrincipale).setTexte(DefilLabel, "texte qui défile");
    Donc je ne pense pas avoir de soucis avec l'EDT, j'ai toujours eu cette habitude de programmation.
    Et cette méthode manipule les composant graphique, donc le problème EDT reste entier (voir la FAQ, toute opération graphique DOIT se faire dans le thread de l'EDT)
    La synchronisation que j'effectue est nécessaire. Le texte à défiler peut varier, comme préciser sur mon premier post.
    un String, c'est immutable, ça ne peux pas changer. Le champ où vous stockez la variable peux changer de valeur, mais pas l'objet String en lui même. Donc le synchronize est à faire sur le conteneur de la String dans ce cas. Aussi, si vous êtes dans le schéma un écrivain d'un coté, un lecteur de l'autre, il y a moyen de s'en sortir sans synchronize

    Je n'ai pas bien saisi ce que vous entendez par Aucune combinaison n’apparaît
    Bah j'ai retappé votre code (sans l'analyser) et la une des chaines apparaissait deux fois d'affilée J'ai pas regardé en détail
    Edit 2 : A l'aide de isEventDispatchThread(), je suis toujours dans l'EDT.
    Votre thread defil ne devrait pas être dans l'EDT :/

  7. #7
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2007
    Messages : 128
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    Et cette méthode manipule les composant graphique, donc le problème EDT reste entier (voir la FAQ, toute opération graphique DOIT se faire dans le thread de l'EDT)
    Non non, c'est pas la méthode qui manipule les composants, c'est la fenetre. La fenetre reçoit les messages puis les dispatches par lui même. Donc je suis dans l'EDT.

    Citation Envoyé par tchize_ Voir le message
    un String, c'est immutable, ça ne peux pas changer. Le champ où vous stockez la variable peux changer de valeur, mais pas l'objet String en lui même. Donc le synchronize est à faire sur le conteneur de la String dans ce cas. Aussi, si vous êtes dans le schéma un écrivain d'un coté, un lecteur de l'autre, il y a moyen de s'en sortir sans synchronize
    Je ne le voyais pas comme ça personnellement.
    On a un pointeur(p1) vers un objet String contenant un autre pointeur(p2) pointant vers une chaîne de caractères.
    Le côté(écrivain) crée un autre objet String dont le pointeur est p3 et qui contient la chaîne pointé par p4.
    Pendant que le côté écrivain demande à l'objet, pointé par p1, de remplacer son ancien pointeur p2 par p4(ceci n'est pas une instruction atomique, si ?), le côté lecteur récupère un pointeur pX(soit p2, soit p4 ou alors une partie de p2 et une partie de p4) contenu dans l'objet String pointé par p1.
    C'est ainsi que je le voyais personnellement.
    Si je suis ce que vous dites, soit le remplacement de p2 par p4 est une instruction atomique soit l'objet String utilise des méthodes synchronisée lors du remplacement/lecture de sa chaîne ?

    Citation Envoyé par tchize_ Voir le message
    Bah j'ai retappé votre code (sans l'analyser) et la une des chaines apparaissait deux fois d'affilée J'ai pas regardé en détail
    Hummm, ça devrait pas. Je ne vois pas cet artefact personnellement.

    Citation Envoyé par tchize_ Voir le message
    Votre thread defil ne devrait pas être dans l'EDT :/
    Ca j'y ai répondu sur mon premier message.


    Edit : En mettant à 100 millisecondes sur votre code, vous ne voyez pas de lag ? Cela viendrait peut être de l'OS alors ? Je suis sur Linux et Mac OS X.

  8. #8
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    je travaille sous linux, j'ai testé mon code jusque 10ms, aucun soucis (17% CPU java + 7% CPU xorg), sachant que je suis sur une machine où flash rame .

    Pour les String, si vous avez ceci:

    class X avec propriété Y de type quelconque (exemple String)

    un thread 1 qui lit en continu X.Y
    un thread 2 qui remplace en permanence X.Y par X.Y2

    dans thread 1 vous lirez, sans synchronisation, dans le pire des cas l'ancienne valeur de X.Y ce qui n'est pas un soucis. La synchronisation deviens nécessaire si on lit plusieurs valeurs liées entre elles (on risque alors de lire une ancien et une nouvelle qui n'ont rien à voir entre elles) et qu'il faut garder leur cohérence.

  9. #9
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2007
    Messages : 128
    Par défaut
    Bizarre :< Moi sur deux systèmes différents, le problème est bien visible aussi bien avec votre code qu'avec le miens et celui apparemment de thelvin.

    Et l'objet String en lui même a t il des méthodes synchronisées ?
    Parce que sinon, on pourrait bien donc se trouver dans le cas où deux actions sont donnés à l’ordonnanceur :
    le premier thread modifie le pointeur : instructions non atomique
    le deuxième thread lis le pointeur : instructions non atomique
    L’ordonnanceur exécute une partie de la première action, puis une partie de la deuxième etc jusqu'à alors aboutir à un pointeur incohérent.
    Ou alors ces instructions actions là sont atomiques, et personnellement je vois mal comment elles pourraient être atomique quand l'objet reçoit ces actions à faire.

  10. #10
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2007
    Messages : 128
    Par défaut
    Bien, j'ai la preuve de ce que j'écris, enfin si je fais pas d'erreur.

    J'ai fais deux codes.
    Dans le premier code :
    Un thread modifie tout le temps le pointeur contenu par l'objet String nommé "entre" par un autre qui contient "test" et l'autre "test1".
    L'autres Thread vérifie si entre contient soit "test" soit "test1" > on a println qui affiche plusieurs fois "Entre contient un pointeur incohérent".
    Dans le deuxième code :
    Avec synchronisation, il n'y a pas de pointeur incohérent.

    Donc l'utilisation de ma synchronisation est bien justifiée.

    Code sans synchronisation : 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
    public class Test {
       static String entre = "blabla";
       static String entre2 = "test";
       static String entre3 = "test1";
       public static void main(String[] argv){
          Thread defil = new Thread() {
    		public void run() {
    				while (true) {
    					entre = entre2;
    					entre = entre3;
    				}
    		}
           };
     
           Thread defil2 = new Thread() {
       		public void run() {
       				while (true) {
       					if (entre.compareTo("test") != 0 && entre.compareTo("test1") != 0 )
       						System.out.println("Entre contient un pointeur incohérent");
       				}
       		}
              };
            defil.setPriority(10);
            defil2.setPriority(10);
            defil.start();
            defil2.start();
       }
    }

    Code avec synchronisation : 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
     
    public class Test {
       static String entre = "blabla";
       static String entre2 = "test";
       static String entre3 = "test1";
       public static void main(String[] argv){
          Thread defil = new Thread() {
    		public void run() {
    				while (true) {
    					synchronized(this) {
    						entre = entre2;
    						entre = entre3;
    					}
    				}
    		}
           };
     
           Thread defil2 = new Thread() {
       		public void run() {
       				while (true) {
    					synchronized(this) {
       						if (entre.compareTo("test") != 0 && entre.compareTo("test1") != 0 ) {
       							System.out.println("Entre contient un pointeur incohérent");
       						}
    					}
       				}
       		}
              };
            defil.setPriority(10);
            defil2.setPriority(10);
            defil.start();
            defil2.start();
       }
    }

    Edit : Je pense qu'il y a un soucis sur mon code.

  11. #11
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    Votre code essaie de décter la cohérence de la string dans le temps (en utilisant plusieurs fois la strings d'affilée), forcément ce n'est plus garanti sans synchronisation.


    Pour reprendre le code de défilement, on doit évidement le modifier pour retirer la synchro;

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void run() {
    					int compteur = 0;
    					while (true) {
    						Thread.sleep(100);
                                                    String texteLocal = texte; // on prend un copie
    			        		int tailleTexte = texteLocal.length();
    			        		compteur = compteur % tailleTexte;
    						jL.setText(texteLocal.substring(compteur, tailleTexte - 1) + texteLocal.substring(0, compteur)); // faut faire passer ça dans l'EDT
    			        		compteur = (compteur + 1) % tailleTexte;
    					}
    				}
    et votre code de test
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
       				while (true) {
                                            String localEntre = entre;
       					if (localEntre.compareTo("test") == 0 && localEntre.compareTo("test1") == 0 )
       						System.out.println("Ca ne peux pas arriver");
                                            if (entre.compareTo("test") == 0 && entre.compareTo("test1") == 0 )
       						System.out.println("Par contre, ceci est probable?");
       				}

  12. #12
    Membre très actif
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2007
    Messages : 128
    Par défaut
    Vous avez raison. La copie et la consultation doivent être deux opérations atomique ou synchronisées. Je parierais plus sur la deuxième option.

    Mon code ne peut pas prouver ce qu'il essaye de prouver. A savoir que si le premier Thread fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    entre = entre2;
    entre = entre3;
    Quand le deuxième Thread finit son premier .compareTo, "entre" a pu être changé deux fois et donc la condition totale peut renvoyer true ce qui est absurde. Pour prouver qu'il y aie une incohérence, si elle existe, il faudrait vérifier en même temps la chaîne de l'objet String "entre" avec "test" et "test1". Mais plutôt que de me casser la tête, je vais m'en tenir à ce que vous dites en me disant ce que j'ai écris plus haut sur ce message.
    Je vais donc retirer ma synchronisation.

    N'empêche ça ne change rien quant à mon lag ^^ Avec ou sans synchro, c'est toujours la même merde ^^'

  13. #13
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Septembre 2008
    Messages
    1 190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 1 190
    Par défaut
    Tu devrais profiler ton appli pour voir ou elle passe le plus de temps.

    Tu saurais ainsi si il y a des goulots d'étranglements.

Discussions similaires

  1. [Conseils] Comment retrouver un problème
    Par Shoryu dans le forum Sondages et Débats
    Réponses: 67
    Dernier message: 03/11/2006, 14h26
  2. Comment gérer les problèmes de connexion sur un idFTP ?
    Par giloutho dans le forum Web & réseau
    Réponses: 2
    Dernier message: 05/12/2005, 19h42
  3. Réponses: 3
    Dernier message: 05/07/2005, 19h07
  4. Réponses: 16
    Dernier message: 24/06/2005, 13h49

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