¿Cómo escalar subprocesos según los núcleos de la CPU?

107

Quiero resolver un problema matemático con múltiples subprocesos en Java. mi problema de matemáticas se puede dividir en unidades de trabajo, que quiero haber resuelto en varios hilos.

No quiero tener una cantidad fija de subprocesos trabajando en él, sino una cantidad de subprocesos que coincida con la cantidad de núcleos de CPU. Mi problema es que no pude encontrar un tutorial sencillo en Internet para esto. Todo lo que encontré son ejemplos con hilos fijos.

¿Cómo se puede hacer esto? ¿Puede proporcionar ejemplos?

Andreas Hornig
fuente

Respuestas:

119

Puede determinar el número de procesos disponibles para la máquina virtual Java mediante el método de tiempo de ejecución estático, availableProcessors . Una vez que haya determinado la cantidad de procesadores disponibles, cree esa cantidad de subprocesos y divida su trabajo en consecuencia.

Actualización : para aclarar aún más, un hilo es solo un objeto en Java, por lo que puede crearlo como lo haría con cualquier otro objeto. Entonces, digamos que llama al método anterior y encuentra que devuelve 2 procesadores. Increíble. Ahora, puede crear un bucle que genere un nuevo subproceso, y divida el trabajo para ese subproceso y lo dispare. Aquí hay un psuedocode para demostrar lo que quiero decir:

int processors = Runtime.getRuntime().availableProcessors();
for(int i=0; i < processors; i++) {
  Thread yourThread = new AThreadYouCreated();
  // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
  yourThread.start();
}

Para obtener más información sobre cómo crear su propio hilo, diríjase a este tutorial . Además, es posible que desee consultar Thread Pooling para la creación de los hilos.

JasCav
fuente
17
Esto es básicamente correcto, pero tenga cuidado con el rendimiento de los procesadores comercializados con "Hyper-Threading" de Intel. En un quad-core, esto devolverá 8 en lugar de 4, pero su rendimiento puede comenzar a disminuir después de 4 hilos, por lo que mis propios puntos de referencia me dicen :)
xcut
Hola, vale, no sabía, que esto es posible. pero cuando divido una tarea en varias unidades de trabajo y necesito la solución de todas las partes para el paso de trabajo final, ¿cómo se hace esto? Cuando tengo varios "yourThreads", ¿cómo uso join () para esto, porque no veo cómo se distinguen estos varios hilos? :) Por cierto: su enlace a Thread Pooling me lleva a ibm.com/developerworks/library/j-jtp0730.html :)
Andreas Hornig
5
Mire el ejemplo aquí: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Le indicará una forma más simplificada de crear y administrar el grupo de subprocesos ... Puede parecer más complicado al principio, pero como ocurre con la mayoría de las cosas, es más complicado porque si fuera más simple, solo alcanzaría las limitaciones antes.
Bill K
62

Probablemente también desee ver el marco java.util.concurrent para estas cosas. Algo como:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Do work using something like either
e.execute(new Runnable() {
        public void run() {
            // do one task
        }
    });

o

    Future<String> future = pool.submit(new Callable<String>() {
        public String call() throws Exception {
            return null;
        }
    });
    future.get();  // Will block till result available

Esto es mucho mejor que lidiar con tus propios grupos de subprocesos, etc.

DaveC
fuente
Hola DaveC, hmmm, no lo sabía antes, así que echaré un vistazo a esto. ¿Y se puede escalar de acuerdo con los núcleos de CPU disponibles? Porque no puedo ver eso en tus breves ejemplos. Saludos cordiales, Andreas
Andreas Hornig
3
java.util.concurrent es altamente escalable
Kristopher Ives
4
Un grupo de tamaño fijo con la cantidad de procesadores disponibles suele ser óptimo para los procesos vinculados a la CPU. El primer ejemplo aquí es todo lo que necesita hacer.
Peter Lawrey
1
Como se indica en el primer comentario de la respuesta aceptada, sería mejor usar la mitad de la cantidad de "Procesadores" informados, por dos razones: 1. si tiene hiperprocesamiento, la cantidad real de procesadores es la mitad de lo que se informa , y 2. deja cierta potencia de procesamiento para que funcione el resto del sistema (SO y otros programas).
Matthieu
10

Opción 1:

newWorkStealingPool deExecutors

public static ExecutorService newWorkStealingPool()

Crea un grupo de subprocesos de robo de trabajo utilizando todos los procesadores disponibles como su nivel de paralelismo de destino.

Con esta API, no necesita pasar un número de núcleos a ExecutorService.

Implementación de esta API desde grepcode

/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

Opcion 2:

newFixedThreadPool API de Executorso other newXXX constructors, que devuelveExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

reemplace nThreads con Runtime.getRuntime().availableProcessors()

Opcion 3:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue)

pasar Runtime.getRuntime().availableProcessors()como parámetro a maximumPoolSize.

Ravindra babu
fuente
4

La forma estándar es el método Runtime.getRuntime (). AvailableProcessors (). En la mayoría de las CPU estándar, habrá devuelto aquí el número de subprocesos óptimo (que no es el número real de núcleos de CPU). Por tanto, esto es lo que estás buscando.

Ejemplo:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

NO olvide cerrar el servicio ejecutor de esta manera (o su programa no se cerrará):

service.shutdown();

Aquí solo un breve resumen de cómo configurar un código MT basado en el futuro (fuera de tema, para ilustración):

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service);
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
    for (String computeMe : elementsToCompute) {
        futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
    }

Luego, debe realizar un seguimiento de cuántos resultados espera y recuperarlos de esta manera:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}
fl0w
fuente
2
la llamada de apagado debería ponerse a prueba finalmente
Christophe Roussy
1
@ChristopheRoussy, tienes mucha razón, modifiqué el fragmento en consecuencia, ¡gracias!
fl0w
3

En la clase Runtime, hay un método llamado availableProcessors (). Puede usar eso para averiguar cuántas CPU tiene. Dado que su programa está vinculado a la CPU, probablemente querrá tener (como máximo) un hilo por CPU disponible.

Eric Petroelje
fuente
Hola Jason y Eric (utilizo un comentario para ambas respuestas, porque básicamente es lo mismo). Está bien, es bueno comprobarlo, pero esta sería la primera parte. Cuando tengo el recuento de núcleos, tengo que tener los hilos tan variables como esta cantidad de núcleos. Probé este ejemplo antes de openbook.galileodesign.de/javainsel5/… (¡alemán!) Y usa un hilo fijo. Pero quiero tener la misma programación usando 2 núcleos en un entorno de doble núcleo y 4 núcleos en un entorno de cuatro núcleos. No quiero cambiarlo manualmente. es posible? ¡GRACIAS! :)
Andreas Hornig
@Andreas: vea las actualizaciones que hice a mi publicación. Creo que eso ayudará a aclarar el problema.
JasCav