2010-05-19 8 views
14

El código Java simple adjunto debería cargar todo el núcleo de la CPU disponible al iniciarlo con los parámetros correctos. Así, por ejemplo, se inicia con¿Por qué este código Java no utiliza todos los núcleos de la CPU?

java VMTest 8 int 0

y comenzará 8 hilos que no hacen nada más que un bucle y la adición de 2 a un entero. Algo que se ejecuta en registros y ni siquiera asigna memoria nueva.

El problema al que nos enfrentamos ahora es que no tenemos una máquina con 24 núcleos cargados (sockets AMD 2 con 12 núcleos cada uno), cuando se ejecuta este sencillo programa (con 24 subprocesos, por supuesto). Cosas similares suceden con 2 programas cada 12 hilos o máquinas más pequeñas.

Así que nuestra sospecha es que la JVM (Sun JDK 6u20 en Linux x64) no escala bien.

¿Alguien ha visto cosas similares o tiene la capacidad de ejecutarlo e informar si funciona bien o no en su máquina (> = 8 núcleos solo por favor)? Ideas?

Lo intenté en Amazon EC2 con 8 núcleos también, pero la máquina virtual parece ser diferente de una caja real, por lo que la carga se comporta de forma totalmente extraña.

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

Información adicional. Acabo de descubrir que la versión de 64 bits del JDK carga los núcleos mucho mejor (alrededor del 90%) que la versión de 32 bits (aproximadamente el 45%). Lo cual es extraño, porque el sistema operativo y la CPU AMD admiten 32 bits y no ejecuto ninguna operación de memoria durante esa prueba. – ReneS

+1

Solo para entender: ¿por qué no usa el método invokeAll (..)? ¿Y por qué no usas callables, por lo que sé, ejecutable no es parte de java.concurrent? – InsertNickHere

+0

También debe observar otros procesos en ejecución. ¿Hizo una ejecución "limpia", sin ningún otro programa/proceso que tomara tiempo de CPU? – InsertNickHere

Respuesta

10

No veo nada malo con su código.

Sin embargo, desafortunadamente, no puede especificar la afinidad del procesador en Java. Entonces, esto realmente se deja al sistema operativo, no a la JVM. Se trata de cómo maneja el sistema operativo los hilos.

Puede dividir sus hilos de Java en procesos separados y envolverlos en código nativo, para poner un proceso por núcleo. Esto, por supuesto, complica la comunicación, ya que será entre procesos en lugar de entre hilos. De todos modos, así es como funcionan las aplicaciones populares de grid computing como boink.

De lo contrario, está a merced del sistema operativo para programar los hilos.

+0

Esta no era la pregunta y ejecutar 2 máquinas virtuales con menos hilos no cambia tanto el juego. – ReneS

+0

¿Qué tal si ejecuta una JVM por núcleo, con dos subprocesos cada uno? Es posible que desee cambiar su pregunta porque respondí "¿Por qué este código Java no utiliza todos los núcleos de la CPU?" –

+0

Disculpa por el malentendido, pero la pregunta es: "... ESTE código de Java ...". Es simple, sin bloqueo, sin sincronización, sin asignación de memoria y aún sin uso de la CPU al 100%. Esto debería crear X hilos simultáneos e independientes. – ReneS

4

Supongo que esto es inherente a la JVM/SO y no necesariamente a su código. Compruebe los diversos documentos de ajuste de rendimiento de JVM de Sun, p. http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf que sugiere usar numactl en Linux para establecer la afinidad.

¡Buena suerte!

+0

Esta opción debería resolver el problema de afinidad (si está implementado) – Justin

+0

Gracias por la sugerencia de las diapositivas y el programa. Intentaremos eso. – ReneS

+0

Parece que hace algunas cosas, pero es complicado. Hay indicadores adicionales tales como -XX: + UseTLAB y -XX: + UseNUMA que se pueden aplicar a la VM para trabajar mejor con la arquitectura subyacente. – ReneS

0

Me he dado cuenta, incluso en C, de que un circuito cerrado a menudo tiene problemas como ese. También verás grandes diferencias dependiendo del sistema operativo.

Dependiendo de la herramienta de informe que esté utilizando, es posible que no informe la CPU utilizada por algunos servicios principales.

Java tiende a ser bastante amigable. Puede intentar lo mismo en Linux, pero establezca la prioridad del proceso en un número negativo y vea cómo actúa.

Establecer prioridades de subprocesos dentro de la aplicación también puede ayudar un poco si su jvm no está usando subprocesos verdes.

Muchas variables.

+1

Gracias, pero ninguna VM está usando hilos verdes más, al menos no que yo sepa en plataformas típicas como Windows, Linux, Solaris, MacOS. – ReneS

+0

@ReneS En los últimos años he trabajado en Java incrustado en cajas de cable y analizadores de espectro (supongo que asumo pocas cosas). Solo decía que había mucho que considerar. –

+0

No hay problema y gracias por su comentario, nunca he visto hilos verdes en mi mundo Java desde hace siglos. – ReneS

1

uname -a 2.6.18-194.11.4.el5 # 1 SMP Mar Sep 21 de 2010 05:04:09 EDT 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, el programa consume el 100% de la CPU, como se muestra por vmstat

Curiosamente vine a este artículo porque estoy sospechando que mi aplicación no está utilizando todos los núcleos como el uso de la CPU no aumenta, pero el tiempo de respuesta comienza a deteriorarse

2

Aparentemente, su máquina virtual se está ejecutando en el llamado modo "cliente", donde todos los subprocesos de Java se asignan a un subproceso de sistema operativo nativo y, en consecuencia, se ejecutan en un único núcleo de CPU. Intente invocar la JVM con el interruptor -server, esto debería corregir el problema.

Si recibe un: Error: no 'server' JVM encontró, que tendrá que copiar el directorio de serverjre\bin directorio del JDK de JRE de bin.

Cuestiones relacionadas