Journal La terre est plate et Java est plus rapide que C++

Posté par  .
Étiquettes : aucune
0
15
avr.
2005
Bonjour,

Je vous propose un petit troll du Vendredi, ainsi qu'un défi.

J'utilise Java Sound pour enregistrer et jouer du son. J'avais, avec le JDK 1.3 des problèmes de pertes de synchro, i.e. Java arrivait pas à suivre la cadence. Les marketteux de chez Sun prétendant que Java Sound était maintenant en Direct Sound (pas celui de M$, celui de Java) depuis la 1.4, utilisant Alsa ou bien DirectX, marchait beaucoup mieux.

Le problème c'est que Java, c'est peut-être plus rapide que C++, mais ça ne sait pas scheduler un thread correctement.

En effet, ma carte son me donne un buffer d'enregistrement de 1s au max (avec Direct Sound on ne peut pas choisir la taille du buffer, et maintenant Sun ne donne plus accès au Java Audio Engine pour l'enregistrement). Du coup, il faut que mon thread d'enregistrement soit schédulé au moins une fois par seconde.

Et ben cette merde de Scheduler qu'il y'a dans la JDK, il est pas capable de me faire ça. A peu près toutes les 30s, mon thread n'est pas schédulé à temps. Pourtant je lui demande un scheduling toutes les 100ms via un wait(100) dans mon thread. Et ce, sans que rien ne tourne en tâche de fond, sauf les processus de mon OS et les quelques threads "system" de la JVM.

Sur un Pentium IV 2.4GHz avec 1 Go de RAM, 0% d'utilisation CPU, le scheduler de la JVM n'est pas foutu de faire en sorte que mon thread soit schedulé avant 1s.

Du coup, le son en Java on peut oublier :)

Je vais coder un bout de C++ pour gérer ça et passer par JNI.

Conclusion la terre est plate (c.f. la page performance sur jsresources.org).
  • # Je prends les paris

    Posté par  . Évalué à 10.

    Tu ne crains pas que la conclusion du débat soit qu'en fait c'est ton truc qui est mal codé ?
    • [^] # Re: Je prends les paris

      Posté par  . Évalué à 6.

      J'ai oublié la partie "défi" qui consistait à me proposer un code qui démontre mon incompétence.
      • [^] # Re: Je prends les paris

        Posté par  (site web personnel) . Évalué à 5.

        Et pourquoi tu ne montres pas ton code tout simplement pour que d'autres y jettent un coup d'oeil et voient quelque chose que peut-être tu n'as pas vu ?
        • [^] # Re: Je prends les paris

          Posté par  . Évalué à 1.

          Désolé, je bosse dans le privé, j'ai pas le droit. Essaie même pas d'imaginer sur quel OS tourne mon code :)
          • [^] # Re: Je prends les paris

            Posté par  (site web personnel) . Évalué à 2.

            Tu peux peut être nous montrer uniquement la partie posant problème ? J'imagine qu'elle n'est pas si secrete que ça ... Sinon comment veux-tu qu'on t'aide ? Si ça se trouve, il y a un bug dans ton code que tu n'as pas trouvé ...

            Sinon c'est comme si je disais : le C sous Linux c'est pas bien parce que mon thread se désynchronise, alors que j'ai surement un gros bug dans mon code ...
            • [^] # Re: Je prends les paris

              Posté par  . Évalué à 2.

              Bon, voilà mon code :
              _in est une TargetDataLine en Java Direct Sound,
              _bigBuf, c'est un plus gros buffer circulaire dans lequel je mets mes données, et que le player va lire pour les jouer sur la carte son par exemple (on va supposer qu'il marche...) (même quand seul le recorder est seul, sans player, j'ai le problème, bon ok, j'ai pas essayé sans la copie vers le bigBuf)


              le corps du run() est :

              while (started) {
              size = _in.available(); // récupère la taille des données dispo
              _in.read(buf, 0, size); // récupère les données (une copie //supplémentaire, je sais)
              _bigBuf.put(buf, 0, size); // écrit dans le buffer circulaire
              Thread.wait(100); // Attends au moins 100ms, si possible moins de 1s
              }

              Ca ne vous avance pas à grand chose. Sinon, y'a des sorties textes de debug au milieu de tout ça, ce qui peut expliquer la chose. M'enfin, je suis plus au taf, donc je peux pas essayer.

              Merci pour la proposition, mais ça vous avance à rien. Y'a pas de deadlock vu que seul un consommateur tourne :) Si mon buffer circulaire buggait, j'aurais des excpetions. Vu qu'il ne fait pas 1Mo ça n'explique de toute façon pas la latence, une copie de 1Mo prenant moins d'une seconde. Une copie de plus de 1Mo déclencherais une excpetion...
              • [^] # Pfff

                Posté par  . Évalué à 0.

                Ca ne vous avance pas à grand chose

                En fait, ça n'avance même à rien, vu que tu donnes un code incomplet.
                Quel est le type de _in ? InputStream ? BufferedInputStream ?
                Quel est le type de buf ? Où est le code de _bigBuf ? Que fait sa méthode put ?

                Évidemment en nous montrant 3 lignes tronquées de ton code ça fait pas du tout avancer le schmilblik, à croire que tu fais exprès, ça doit faire partie de ton troll.

                Tout ce que ça montre, c'est que tu es plus habitué à c++ qu'à java, parce que préfixer d'un '_' le nom des attributs privés est un idiome c++ ; en java, on se contente du modifiant private.
                • [^] # Re: Pfff

                  Posté par  (site web personnel) . Évalué à -3.

                  > préfixer d'un '_' le nom des attributs privés est un idiome c++

                  Naon, en c++ comme en c, les identifieurs commençant par un underscore sont reservés pour l'usage interne du compilo et de la bibliothèque standard !
                • [^] # Re: Pfff

                  Posté par  (site web personnel) . Évalué à 3.

                  > Quel est le type de _in ?

                  Bien relire le message du môsieur ...
                  • [^] # Re: Pfff

                    Posté par  . Évalué à 2.

                    Mon soft est censé cohabiter avec du hard, qui m'envoie 1s de son toutes les secondes. Donc j'utilise directement une TargetDataLine, et je fait un read dessus.

                    Et en mettant des currentTimeMillis() partout, j'ai vu que mon appel à _in.read() avec comme dernier paramètre le résultat renvoyé par _in.available(), qui ne devrait donc pas bloquer, prend la plupart de temps à peu près 0 ms et de temps à autre 1000ms, comme si il bloquait pour attendre un remplissage complet du buffer interne de la carte son, qui dur 1000ms.

                    Le scheduler n'est pas en cause, ni ma synchro, le wait() du thread dure grosso modo ce qu'il faut. D'ailleurs, chose bizarre, il dure parfois moins que le temps demandé.

                    Je vais essayer de creuser le problème. Par exemple ma carte est une fausse full duplex.
                    • [^] # Re: Pfff

                      Posté par  . Évalué à 5.

                      Ouais, c'était bien une histoire de TargetDataLine.read().

                      Je sais pas si c'est dû au driver de ma carte son, à DirectX, ou au JDK mais maintenant que je lis 1000 octets de moins que ce que me donne le available() et ben jai plus de read() bloquant.

                      Toutes mes excuses pour les critiques du scheduler, qui n'était pas en cause, de même que le GC.
              • [^] # Re: Je prends les paris

                Posté par  . Évalué à 4.

                Le Thread.wait(100), comme tu le dis en commentaire, signifie « attends au moins 100 ms ».
                C'est tout.
                Java n'est pas temps réel (ton OS non plus, je pense), il ne promet pas que le retour se fera avant un délai donné (il peut très bien finir dans 25 ans).

                Ton problème vient peut-être des entrées-sorties ou du garbage collector qui viennent perturber l'ordonnanceur.

                Ensuite, ce n'est parce que tu vas passer par du JNI que la machine virtuelle ou les entrées-sorties ne vont pas continuer à venir perturber le fonctionnement de ton prog.
                Sans parler des problèmes que l'on peut avoir en mélangeant les threads natifs aux threads Java.

                Enfin, si tu n'as aucun raison pour faire un Thread.wait(100) plutôt qu'un Thread.wait(10), autant faire un Thread.yield(). De cette façon, ton thread reprendra la main dès que possible.
      • [^] # Bien tenté...

        Posté par  . Évalué à 7.

        J'ai oublié la partie "défi" qui consistait à me proposer un code qui démontre mon incompétence.

        Bien tenté, mais dans la pratique ça fonctionne en sens inverse: c'est à toi de nous montrer ton code pour démontrer ta compétence.
    • [^] # Re: Je prends les paris

      Posté par  . Évalué à 2.

      Quiconque a déjà écrit un prod/cons en Java connaît le problème.

      Si tu fais juste un petit programme "cas décole" qui ne fait aucun traitement et se contente de faires des println dans la console à chaque production et consommation, tu observeras des pauses assez fréquentes.

      Ce qui me fait halluciner c'est que ces pauses peuvent durer plus d'une seconde.

      Je me demande si ce sontles programmeurs qui ont implémentés le truc qui sont incompétents ou bien, pire, si c'est une faille de la spec d'implémentation des API de thread qu'ils ont en interne chez Sun.

      Quand on voit la qualité de Solaris, on se dit qu'ils pourraient débaucher quelques hackers pour les mettre à bosser sur Java.
      • [^] # Re: Je prends les paris

        Posté par  (site web personnel) . Évalué à 3.

        Je n'ai jamais eu de problème de synchro de threads en Java ...

        Peut-être peux-tu nous montrer ton code ?
      • [^] # Re: Je prends les paris

        Posté par  (site web personnel) . Évalué à 5.

        <neophite>
        peut etre le garbage colector qui tourne
        </neophite>
        • [^] # Re: Je prends les paris

          Posté par  (site web personnel) . Évalué à 1.

          Voir le Java 1.5, il y a des options pour affiner le fonctionnement du ramasse-miettes, de mémoire on peut spécifier qu'on veut qu'il s'active obligatoirement tous les XXX secondes, et on peut spécifier qu'à chaque activation il ne doit pas prendre plus de YYY secondes.

          (y'avait un article là-dessus dans un numéro de GNU-Linux Magazine)

          Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN

          • [^] # Re: Je prends les paris

            Posté par  . Évalué à 1.

            Et il y a certainement des options pour demander au GC de loguer quand il se met à travailler. Avec un top, il y a certainement moyen de voir si le système part au swap (je suppose que tu as un top, même si c'est un système exotique?) (de toute façon si ça supporte java ça peut pas être une petite plateforme).
      • [^] # Re: Je prends les paris

        Posté par  . Évalué à 4.

        Un petit test qui résoud certains problèmes : augmente la taille initiale de la ram alouée à la JVM (-Xms128m pour mettre 128MB par exemple).

        Au cours de tests d'applis Web sous Tomcat, j'ai utilisé un outil pas mal qui s'appelle Jprobe profiler (ca pue c'est pas libre). Pour constater que les latences de mon application étaient dues aux allocations mémoire. En gros quand la JVM augmente (ou diminue) la mémoire disponible, elle ne peut rien faire d'autre ou quasiment... Sachant qu'une application Java est assez consommatrice de mémoire, j'avais des pauses toutes les 10 secondes.

        --
        Thomas <et voilà que je me met à donner des conseils Java. Tout fout le camp>
        • [^] # Re: Je prends les paris

          Posté par  . Évalué à 3.

          ah ben alors c'est vraiment lamentable ... car un exemple de producteur/consomateur ca ne devrait pas faire grossir l'occupation memoire. Ca peut eventuellement grossir au debut mais ensuite doit se stabiliser . Donc les "pauses" n'ont aucune raison d'etre.
          • [^] # Re: Je prends les paris

            Posté par  . Évalué à 1.

            Peut-être que la JVM provisionne (pour rien, mais elle ne peut pas tout deviner) de la RAM au fur et à mesure ?
          • [^] # Re: Je prends les paris

            Posté par  (site web personnel) . Évalué à 3.

            Si, ça fait grossir jusqu'au prochain passage du ramasse-miettes.

            Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN

        • [^] # Re: Je prends les paris

          Posté par  (site web personnel) . Évalué à 0.

          Thomas <et voilà que je me met à donner des conseils Java. Tout fout le camp>

          Heu... LE Thomas que je connais ? En effet, tout fou le camp :)
      • [^] # Re: Je prends les paris

        Posté par  . Évalué à 3.

        Est-ce que ça ne viendrait pas aussi du choix du modèle de threading de la JVM ?

        Il me semble, si je me souviens bien mes cours de temps réel (en Java, vivi), qu'il y a au moins deux modèles (green box, native ou standard, je ne sais plus) et que cela peut avoir une incidence sur le scheduling des threads et leurs synchro (pas de pointeur en tête, désolé).
        • [^] # Re: Je prends les paris

          Posté par  . Évalué à 1.

          Ca c'est intéressant comme réponse, je songeais justement à lire les howto de IBM sur le paramètrage de la JVM.

          En tout cas, question RT, effectivement Java ne donne pas plus de garantie que Linux vanilla ou Windows en matière de schéduling. Par contre dans ces deux derniers cas, dans la pratique, on n'a pas de latence de 1s.

          Question pause, et ben oui elles y sont. Et effectivement, je me suis démerder pour ne plus faire d'alloc dans ma boucle de pompage, et les pauses y sont encore.
          • [^] # Re: Je prends les paris

            Posté par  . Évalué à 2.

            J'avoue je ne comprends pas bien ton probleme.

            "A peu près toutes les 30s, mon thread n'est pas schédulé à temps."

            Ca veut dire quoi exactement ?

            Le wait ne se fait pas ? (très improbable).

            Les waits n'étant pas "constants", tu as une attente plus longue (ou plus courte) au bout d'un certain temps ? (plus probable).

            Que veux tu faire exactement ?
            • [^] # Re: Je prends les paris

              Posté par  . Évalué à 2.

              Son tampon a une taille de 1 seconde, et son thread reste apparemment au moins 1 seconde sans s'exécuter, ce qui fait que le tampon se retrouve vide.
              D'ou bizarrerie: le thread n'est censé ne dormir que 100ms.
              • [^] # Re: Je prends les paris

                Posté par  . Évalué à 3.

                C'est donc que le pb n'est pas dans le wait(100), mais probable dans les opérations de lecture/copie de buffer ; dans la mesure où il ne fait pas du temps réels, ie pas d'appel de fonctions qui peuvent garantir un temps d'execution minimum, tout peut arriver, y compris des "gels" de plus d'1 seconde - probablement le ramasse-miette ou autre joyeuseté de la JVM.
      • [^] # Re: Je prends les paris

        Posté par  (site web personnel) . Évalué à 0.


        Quand on voit la qualité de Solaris, on se dit qu'ils pourraient débaucher quelques hackers pour les mettre à bosser sur Java.

        Je crois que les hackers en question préfereraient démissioner que de travailler sur Java...
  • # J'ai pas constaté.

    Posté par  . Évalué à -3.

    Je fais pas du temps réel, mais j'utilise des applications java diverses, jedit, Eclipse...
    Je ne constate pas des pauses d'une seconde périodiques.

    Ton problème n'est probablement pas généralisé à tout java. Reste a trouver ce qui le déclenche.
    • [^] # Re: J'ai pas constaté.

      Posté par  (site web personnel) . Évalué à 0.

      j'utilise des applications java diverses, jedit, Eclipse...
      Non sérieux, c'est bien connu, Eclipse il ralenti jamais, surtout quand le GC passe dans le coin, y'a jamais plus de 2 µs entre le click de la souris et la réaction de l'IHM, c'est bien connu, surtout au bout de 3h d'utilisation, sous Linux s'il vous plaît...
      Bon allez j'ai assez ri, hop -->[]
  • # vu sur le site de SUN

    Posté par  . Évalué à 9.

  • # javasound

    Posté par  (site web personnel) . Évalué à 1.

    franchement javaSound c'est une très grosse merde au niveau performances (et encore je pèse mes mots..). La seule solution que j'ai trouvé et JNI & libasound (alsa) qui marche plutôt bien mais qui n'est pas portable pour Windows
    • [^] # Re: javasound

      Posté par  . Évalué à 5.

      J'ai l'impression que le dernier JavaSound, qui utilise alsa en accès direct est pas mal. Le seul problème c'est leur scheduler et leur GC.
  • # une "solution"

    Posté par  . Évalué à 4.

    plus un contournement qu'autre chose : t'as essaye de remplacer ton thread par un Timer?
    ca correspond plus a ce que tu veux faire visiblement (ie : executer une tache toutes les n ms plutot que d'executer une tache en continue qui dort 90% du temps)
    • [^] # Re: une "solution"

      Posté par  . Évalué à 2.

      Le timer fait lui aussi appelle à thread, c'est une classe codée en Java. Donc je préfère ré-inventer la roue :)
  • # \o/

    Posté par  (site web personnel) . Évalué à -3.

    > Le problème c'est que Java, c'est peut-être plus rapide que C++

    c'est une blague j'espere :)
  • # Lâcher de trolls

    Posté par  . Évalué à -2.

    Je vous propose un petit troll du Vendredi, ainsi qu'un défi.

    J'utilise Java Sound pour enregistrer et jouer du son. J'avais, avec le JDK 1.3 des problèmes de pertes de synchro, i.e. Java arrivait pas à suivre la cadence.


    Tu te prends pour Pierre Tramo ?

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.