2009-07-29 18 views
37

Estoy escribiendo una aplicación que tendrá varios subprocesos en ejecución, y quiero acelerar el uso de CPU/memoria de esos subprocesos.¿Disminuye el uso de CPU/memoria de un subproceso en Java?

Hay un similar question for C++, pero quiero intentar y evitar el uso de C++ y JNI si es posible. Me doy cuenta de que esto podría no ser posible utilizando un lenguaje de nivel superior, pero tengo curiosidad por ver si alguien tiene alguna idea.

EDIT: Se ha añadido una recompensa; Me gustaría algunas ideas realmente buenas y bien pensadas sobre esto.

EDIT 2: La situación que necesito para esto es ejecutar el código de otras personas en mi servidor. Básicamente es un código completamente arbitrario, con la única garantía de que habrá un método principal en el archivo de clase. Actualmente, varias clases completamente dispares, que se cargan en tiempo de ejecución, se ejecutan simultáneamente como subprocesos separados.

De la forma en que está escrito, sería una molestia refactorizar para crear procesos separados para cada clase que se ejecuta. Si esa es la única buena forma de limitar el uso de la memoria a través de los argumentos de VM, entonces que así sea. Pero me gustaría saber si hay una manera de hacerlo con hilos. Incluso como un proceso separado, me gustaría poder limitar de alguna manera el uso de la CPU, ya que como mencioné anteriormente, varios de ellos se ejecutarán a la vez. No quiero un bucle infinito para acaparar todos los recursos.

EDIT 3: Una manera fácil de aproximar el tamaño del objeto es con las clases Instrumentation de java; específicamente, el método getObjectSize. Tenga en cuenta que se necesita alguna configuración especial para usar esta herramienta.

+0

¿Qué estás usando para un modelo de subprocesamiento? ¿Los ejecutores de tareas Java? –

+0

Además, ¿dónde están sus cuellos de botella en esta aplicación? ¿Base de datos? IO? –

+0

La única vez que puedo pensar dónde querría limitar la CPU era si la duración de la batería era un problema (y entonces se hace su pregunta, ¿cómo descubro cuando estoy haciendo cosas computacionalmente caras en un dispositivo de batería limitada?) . De lo contrario, ¿por qué hacer que el usuario espere más de lo necesario? Si desea mantener la capacidad de respuesta del sistema, use prioridad de subproceso bajo en lugar de intentar limitar el uso de CPU. –

Respuesta

30

Si entiendo su problema, una forma sería drenar los hilos de manera adaptativa, del mismo modo que la reproducción de video se realiza en Java. Si sabes que quieres un 50% de utilización del núcleo, tu algoritmo debería dormir aproximadamente 0.5 segundos, potencialmente distribuido en un segundo (por ejemplo, 0.25 segundos de computación, 0.25 segundos de sueño, e.t.c.). Aquí hay un example de mi reproductor de video.

long starttime = 0; // variable declared 
//... 
// for the first time, remember the timestamp 
if (frameCount == 0) { 
    starttime = System.currentTimeMillis(); 
} 
// the next timestamp we want to wake up 
starttime += (1000.0/fps); 
// Wait until the desired next time arrives using nanosecond 
// accuracy timer (wait(time) isn't accurate enough on most platforms) 
LockSupport.parkNanos((long)(Math.max(0, 
    starttime - System.currentTimeMillis()) * 1000000)); 

Este código dormirá en función de los fotogramas/segundo valor.

Para acelerar el uso de memoria, puede ajustar la creación de objetos en un método de fábrica y usar algún tipo de semáforo con permisos limitados como bytes para limitar el tamaño total estimado del objeto (es necesario estimar el tamaño de varios objetos racionar el semáforo).

package concur; 

import java.util.Random; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Semaphore; 
import java.util.concurrent.TimeUnit; 

public class MemoryLimited { 
    private static Semaphore semaphore = new Semaphore(1024 * 1024, true); 
    // acquire method to get a size length array 
    public static byte[] createArray(int size) throws InterruptedException { 
     // ask the semaphore for the amount of memory 
     semaphore.acquire(size); 
     // if we get here we got the requested memory reserved 
     return new byte[size]; 
    } 
    public static void releaseArray(byte[] array) { 
     // we don't need the memory of array, release 
     semaphore.release(array.length); 
    } 
    // allocation size, if N > 1M then there will be mutual exclusion 
    static final int N = 600000; 
    // the test program 
    public static void main(String[] args) { 
     // create 2 threaded executor for the demonstration 
     ExecutorService exec = Executors.newFixedThreadPool(2); 
     // what we want to run for allocation testion 
     Runnable run = new Runnable() { 
      @Override 
      public void run() { 
       Random rnd = new Random(); 
       // do it 10 times to be sure we get the desired effect 
       for (int i = 0; i < 10; i++) { 
        try { 
         // sleep randomly to achieve thread interleaving 
         TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10); 
         // ask for N bytes of memory 
         byte[] array = createArray(N); 
         // print current memory occupation log 
         System.out.printf("%s %d: %s (%d)%n", 
          Thread.currentThread().getName(), 
          System.currentTimeMillis(), array, 
          semaphore.availablePermits()); 
         // wait some more for the next thread interleaving 
         TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10); 
         // release memory, no longer needed 
         releaseArray(array); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
     }; 
     // run first task 
     exec.submit(run); 
     // run second task 
     exec.submit(run); 
     // let the executor exit when it has finished processing the runnables 
     exec.shutdown(); 
    } 
} 
+0

Me gustaría hacer esto más fácil para que las personas lo miren rápidamente; ¿te importaría agregar un poco más de detalle sobre lo que estás haciendo y comentar el código? –

+0

No servirá de mucho ya que el código de muestra ya es demasiado largo sin comentarios. Pero lo haré. – akarnokd

+0

Esta es una manera realmente inteligente de manejar la administración de memoria, y es la única buena dirección que se ha dado hasta ahora. Como no funcionará para mí debido a EDIT 2 en mi OP, pero probablemente terminaré usando Aspects para interceptar nuevos objetos que se creen para ese hilo, y realizar un seguimiento de él con un semáforo para cada Thread. –

5

Puede obtener mucha información sobre el uso de CPU y memoria a través del JMX, pero no creo que permita ninguna manipulación activa.

Para controlar el uso de la CPU hasta cierto punto, puede usar Thread.setPriority().

En cuanto a la memoria, no existe la memoria por subproceso. El concepto mismo de los hilos de Java significa memoria compartida. La única forma de controlar el uso de la memoria es a través de las opciones de línea de comando como -Xmx, pero no hay forma de manipular la configuración en el tiempo de ejecución.

1

Puede asignar diferentes prioridades a los hilos para que el hilo más relevante se programe con más frecuencia.

Mire este answer para ver si eso ayuda.

Cuando todo el hilo conductor tiene la misma prioridad que pueden funcionar de esta manera:

t1, t2, t3,  t1, t2, t3, t1, t2, t3 

Cuando se asigna una prioridad diferente a uno de ellos puede verse como:

t1, t1, t1, t1, t2, t1, t1, t1 t3. 

Eso es , el primer hilo se ejecuta "más a menudo" que el resto.

+0

En la práctica esto no ayudará porque bajo Java normalmente todos los hilos se ejecutarán con la misma prioridad. Entonces, la prioridad normalmente se ignora. –

0

Thread.setPriority() puede ayudar, pero no le permite limitar la CPU utilizada por un hilo. De hecho, no he oído hablar de ninguna biblioteca Java que haga esto.

Podría ser posible implementar dicha instalación siempre que sus hilos estén preparados para cooperar.La clave es hacer que los hilos se llamen periódicamente a un programador personalizado y hacer que el programador controle el uso de la CPU con JMX. Pero el problema es que si un hilo no hace que el programador llame con la suficiente frecuencia, puede exceder los límites de aceleración. Y no hay nada que puedas hacer sobre un hilo que se atasca en un ciclo.

Otra ruta teórica para implementar sería el uso de aislamientos. Desafortunadamente, será difícil encontrar una JVM de propósito general que implemente aislamientos. Además, las API estándar solo te permiten controlar el aislamiento, no los hilos dentro del aislado.

0

La única manera en que puede limitar el uso de la CPU de subprocesos es mediante un bloque en un recurso o para llamar a yield() con frecuencia.

Esto no limita el uso de CPU por debajo del 100%, pero le da a otros subprocesos y procesa más segmentos de tiempo.

-1

Para reducir la CPU, si quiere dormir sus hilos en el interior de común si y mientras bucles.

while(whatever) { 
    //do something 
    //Note the capitol 'T' here, this sleeps the current thread. 
    Thread.sleep(someNumberOfMilliSeconds); 
} 

Dormir durante unos cientos de milisegundos reducirá en gran medida el uso de la CPU con poco o ningún resultado notable en el rendimiento.

En cuanto a la memoria, ejecutaba un generador de perfiles en los subprocesos individuales y realizaba algunos ajustes de rendimiento. Si estrangulaste la cantidad de memoria disponible para enhebrar, creo que es probable que exista una excepción de falta de memoria o un hilo muerto de hambre. Confío en que la JVM proporcionará tanta memoria como el hilo necesario y trabajará para reducir el uso de memoria al mantener solo objetos esenciales en el alcance en un momento dado.

5

Cuidados de Java Forums. Básicamente cronometrando tu ejecución y luego esperando cuando tomas demasiado tiempo. Como se menciona en el hilo original, ejecutar esto en un hilo separado e interrumpir el hilo de trabajo proporcionará resultados más precisos, al igual que promediar los valores a lo largo del tiempo.

import java.lang.management.*; 

ThreadMXBean TMB = ManagementFactory.getThreadMXBean(); 
long time = new Date().getTime() * 1000000; 
long cput = 0; 
double cpuperc = -1; 

while(true){ 

if(TMB.isThreadCpuTimeSupported()){ 
    if(new Date().getTime() * 1000000 - time > 1000000000){ //Reset once per second 
     time = new Date().getTime() * 1000000; 
     cput = TMB.getCurrentThreadCpuTime(); 
    } 

    if(!TMB.isThreadCpuTimeEnabled()){ 
     TMB.setThreadCpuTimeEnabled(true); 
    } 

    if(new Date().getTime() * 1000000 - time != 0) 
     cpuperc = (TMB.getCurrentThreadCpuTime() - cput)/(new Date().getTime() * 1000000.0 - time) * 100.0;     
    } 
//If cpu usage is greater then 50% 
if(cpuperc > 50.0){ 
    //sleep for a little bit. 
    continue; 
} 
//Do cpu intensive stuff 
} 
1

Si ejecuta los hilos en un proceso separado que puede limitar el uso de memoria y limitar el número de CPU o cambiar la prioridad de estos hilos.

Sin embargo, todo lo que haga es probable que agregue sobrecarga y complejidad, que a menudo es contraproducente.

A menos que pueda explicar por qué querría hacer esto (por ejemplo, tiene una biblioteca mal escrita en la que no confía y no puede obtener soporte), le sugiero que no la necesite.

La razón por la que no es fácil restringir el uso de la memoria es que solo hay un montón que se comparte. Por lo tanto, un objeto que se utiliza en un hilo se puede usar en otro y no se asigna a un hilo u otro.

Limitar el uso de CPU significa detener todos los hilos para que no hagan nada, sin embargo, un mejor enfoque es asegurarse de que el hilo no desperdicie la CPU y solo esté activo haciendo el trabajo necesario, en cuyo caso no querrás detenerlos haciéndolo.

+1

Agregó el razonamiento para esto en el PO. –

+0

Un ciclo infinito consumirá solo un núcleo. Muchos servidores nuevos tienen de 4 a 16 núcleos, por lo que podría no ser tan problemático como solía ser. Nota: un proceso independiente se puede matar de forma segura por cualquier motivo. –

1

¿Por qué no en lugar de hacer "subprocesos" hacer multitarea cooperativa, sería interesante ver si puede manipular http://www.janino.net/ para ejecutar un programa durante una cierta cantidad de tiempo/conjunto de instrucciones, luego detener y ejecutar el siguiente programa. Al menos de esa manera es justo, dale a todos la misma porción de tiempo ...

Cuestiones relacionadas