2010-05-19 30 views
14

Le code Java simple ci-joint doit charger tout le cpu core disponible lors du démarrage avec les bons paramètres. Ainsi, par exemple, vous commencez avecPourquoi ce code Java n'utilise-t-il pas tous les cœurs de processeur?

java VMTest 8 int 0

et il va commencer 8 fils qui ne font rien d'autre que boucle et en ajoutant 2 à un nombre entier. Quelque chose qui s'exécute dans les registres et n'attribue même pas de nouvelle mémoire. Le problème auquel nous sommes confrontés maintenant, c'est que nous ne disposons pas d'une machine 24 cœurs (AMD 2 sockets avec 12 cœurs chacun), lors de l'exécution de ce simple programme (avec 24 threads bien sûr). Des choses similaires se produisent avec 2 programmes de 12 threads ou de plus petites machines.

Donc, notre soupçon est que la JVM (Sun JDK 6u20 sous Linux x64) ne se met pas bien à l'échelle.

Quelqu'un at-il vu des choses similaires ou a-t-il la capacité de les faire fonctionner et de signaler s'il fonctionne bien sur sa machine (> = 8 coeurs seulement s'il vous plaît)? Des idées?

J'ai essayé sur Amazon EC2 avec 8 cœurs aussi, mais la machine virtuelle semble fonctionner différemment d'une vraie boîte, donc le chargement se comporte de manière totalement étrange.

package com.test; 

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 
import java.util.concurrent.TimeUnit; 

public class VMTest 
{ 
    public class IntTask implements Runnable 
    { 
     @Override 
     public void run() 
     { 
      int i = 0; 

      while (true) 
      { 
       i = i + 2; 
      } 
     } 
    } 
    public class StringTask implements Runnable 
    { 
     @Override 
     public void run() 
     { 
      int i = 0; 

      String s; 
      while (true) 
      { 
       i++; 
       s = "s" + Integer.valueOf(i); 
      } 
     } 
    } 
    public class ArrayTask implements Runnable 
    { 
     private final int size; 
     public ArrayTask(int size) 
     { 
      this.size = size; 
     } 
     @Override 
     public void run() 
     { 
      int i = 0; 

      String[] s; 
      while (true) 
      { 
       i++; 
       s = new String[size]; 
      } 
     } 
    } 

    public void doIt(String[] args) throws InterruptedException 
    { 
     final String command = args[1].trim(); 

     ExecutorService executor = Executors.newFixedThreadPool(Integer.valueOf(args[0])); 
     for (int i = 0; i < Integer.valueOf(args[0]); i++) 
     { 
      Runnable runnable = null; 
      if (command.equalsIgnoreCase("int")) 
      { 
       runnable = new IntTask(); 
      } 
      else if (command.equalsIgnoreCase("string")) 
      { 
       runnable = new StringTask(); 
      } 
      Future<?> submit = executor.submit(runnable); 
     } 
     executor.awaitTermination(1, TimeUnit.HOURS); 
    } 

    public static void main(String[] args) throws InterruptedException 
    { 
     if (args.length < 3) 
     { 
      System.err.println("Usage: VMTest threadCount taskDef size"); 
      System.err.println("threadCount: Number 1..n"); 
      System.err.println("taskDef: int string array"); 
      System.err.println("size: size of memory allocation for array, "); 
      System.exit(-1); 
     } 

     new VMTest().doIt(args); 
    } 
} 
+0

Informations supplémentaires. Je viens de découvrir que la version 64 bits du JDK charge mieux les cœurs (environ 90%) que la version 32 bits (environ 45%). Ce qui est étrange, car le système d'exploitation et le cpu AMD supportent 32bit et je n'exécute aucune opération de mémoire pendant ce test. – ReneS

+1

Juste pour comprendre - pourquoi n'utilisez-vous pas la méthode invokeAll (..)? Et pourquoi n'utilisez-vous pas les callables, autant que je sache, runnable ne fait pas partie de java.concurrent? – InsertNickHere

+0

Vous devez également surveiller les autres processus en cours d'exécution. Avez-vous fait une course "propre", sans autres progamms/processus prenant le temps de cpu? – InsertNickHere

Répondre

10

Je ne vois rien de mal avec votre code.

Cependant, vous ne pouvez malheureusement pas spécifier l'affinité du processeur dans Java. Donc, c'est en fait à l'OS, pas à la JVM. Tout dépend de la façon dont votre système d'exploitation gère les threads.

Vous pouvez diviser vos threads Java en processus séparés et les intégrer dans du code natif, pour mettre un processus par cœur. Cela complique bien entendu la communication, car elle sera inter-processus plutôt qu'inter-thread. Quoi qu'il en soit, c'est ainsi que fonctionnent les applications de calcul en grille populaires comme boink.

Sinon, vous êtes à la merci du système d'exploitation pour planifier les threads.

+0

Ce n'était pas la question et l'exécution de 2 machines virtuelles avec moins de threads ne change pas beaucoup le jeu. – ReneS

+0

Que diriez-vous d'exécuter une JVM par cœur, avec deux threads chacun? Vous pourriez vouloir changer votre question parce que j'ai répondu «Pourquoi ce code Java n'utilise-t-il pas tous les cœurs de processeur? –

+0

Désolé pour le malentendu, mais la question est: "... CE code Java ...". C'est simple, pas de verrouillage, pas de synchronisation, pas d'allocation de mémoire et toujours pas 100% d'utilisation du processeur. Cela devrait créer X threads concurrents et indépendants. – ReneS

4

Je suppose que cela est inhérent à la JVM/OS et pas nécessairement votre code. Vérifiez les divers documents d'optimisation des performances JVM de Sun, par ex. http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf qui suggère d'utiliser numactl sous Linux pour définir l'affinité.

Bonne chance!

+0

Cette option devrait résoudre le problème de l'unité (si elle est implémentée) – Justin

+0

Merci pour l'indication des diapositives et du programme. Je vais essayer ça. – ReneS

+0

Semble faire quelques trucs, mais c'est difficile. Il existe des indicateurs supplémentaires tels que -XX: + UseTLAB et -XX: + UseNUMA qui peuvent être appliqués à la machine virtuelle pour mieux fonctionner avec l'architecture sous-jacente. – ReneS

0

J'ai remarqué même sur C qu'une boucle serrée a souvent des problèmes comme celui-là. Vous verrez également de très grandes différences en fonction du système d'exploitation. En fonction de l'outil de génération de rapports que vous utilisez, il se peut qu'il ne signale pas la CPU utilisée par certains services principaux.

Java a tendance à être plutôt sympathique. Vous pourriez essayer la même chose sous Linux mais définir la priorité du processus à un nombre négatif et voir comment cela agit.

La définition des priorités de threads à l'intérieur de l'application peut aussi aider un peu si votre jvm n'utilise pas de threads verts.

Beaucoup de variables.

+1

Merci, mais aucune machine virtuelle n'utilise plus les threads verts, du moins pas à ma connaissance sur les plates-formes typiques telles que Windows, Linux, Solaris, MacOS. – ReneS

+0

@ReneS Au cours des dernières années, j'ai travaillé sur Java embarqué dans des câblosélecteurs et des analyseurs de spectre - je ne fais que quelques suppositions comme ça - je disais juste qu'il y avait beaucoup à considérer. –

+0

Pas de problème et merci pour votre commentaire, je n'ai jamais vu les fils verts dans mon monde Java depuis des lustres. – ReneS

1

uname -a 2.6.18-194.11.4.EL5 # 1 SMP Mar 21 05:04:09 EDT sept 2010 x86_64 x86_64 x86_64 GNU/Linux

Intel (R) Xeon (R) CPU E5530 @ 2.40GHz http://browse.geekbench.ca/geekbench2/view/182101

Java 1.6.0_20-b02

16cores, le programme consommé cpu 100% comme indiqué par vmstat

Il est intéressant que je suis venu à cet article parce que je soupçonnais ma demande n'utilise pas tous les noyaux que l'utilisation de cpu augmente jamais, mais le temps de réponse commence à se détériorer

2

Apparemment, votre machine virtuelle fonctionne en mode "client", où tous les threads Java sont mappés sur un thread de système d'exploitation natif et sont par conséquent exécutés par un seul cœur de processeur. Essayez d'invoquer la JVM avec le commutateur -server, cela devrait corriger le problème.

Si vous obtenez un: Error: no 'server' JVM trouvé, vous devrez copier le répertoire server à partir d'un répertoire de jre\bin à JDK Environnement Java bin.