Pregunta

Estoy escribiendo una aplicación que tendrá varios subprocesos en ejecución y querrá acelerar el uso de la CPU / memoria de esos subprocesos.

Hay una pregunta similar para C ++ , pero quiero intentar evitar usar 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.

EDITAR: Se agregó 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.

La forma en que está escrito, sería una pena refactorizar la creación de procesos separados para cada clase que se ejecuta. Si esa es la única manera buena de limitar el uso de la memoria a través de los argumentos de la VM, 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 estos se ejecutarán a la vez. No quiero un bucle infinito para acaparar todos los recursos.

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

¿Fue útil?

Solución

Si entiendo su problema, una forma sería dormir de forma adaptativa los hilos, de manera similar a como se hace la reproducción de video en Java. Si sabe que desea una utilización básica del 50%, su algoritmo debería dormir aproximadamente 0,5 segundos, potencialmente distribuido en un segundo (por ejemplo, 0.25 segundos de cálculo, 0.25 segundos de sueño, e.t.c.). Aquí hay un ejemplo 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 se suspenderá según los marcos / segundo valor.

Para acelerar el uso de la memoria, puede incluir la creación de su objeto 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 (debe estimar el tamaño de varios objetos para 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();
    }
}

Otros consejos

Puede obtener mucha información sobre el uso de la CPU y la memoria a través de 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 tal cosa como la memoria por hilo. El mismo concepto de hilos de Java significa memoria compartida. La única forma de controlar el uso de la memoria es a través de las opciones de la línea de comandos como -Xmx, pero no hay forma de manipular la configuración en tiempo de ejecución.

Cuidado de Foros de Java . Básicamente cronometra tu ejecución y luego espera cuando te tomas demasiado tiempo. Como se menciona en el subproceso original, ejecutar esto en un subproceso separado e interrumpir el subproceso de trabajo dará resultados más precisos, al igual que el promedio de 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
}

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

Mire esto responder para ver si eso ayuda.

Cuando todos los subprocesos en ejecución tienen la misma prioridad, pueden ejecutarse así:

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

Cuando asignas una prioridad diferente a uno de ellos, puede que parezca:

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

Es decir, el primer hilo se ejecuta " más a menudo " que el resto.

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

Sin embargo, cualquier cosa que haga es probable que agregue sobrecarga y complejidad, lo 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 por la que no puede recibir asistencia), sugeriría que no es necesario.

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 usa en un hilo se puede usar en otro y no se asigna a un hilo u otro.

Limitar el uso de la CPU significa detener todos los subprocesos para que no hagan nada, sin embargo, un mejor enfoque es asegurarse de que el subproceso no desperdicie la CPU y que solo esté activo haciendo el trabajo que debe hacerse, en cuyo caso No querría que dejen de hacerlo.

¿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 detenga y ejecute el siguiente programa. Al menos de esa manera es justo, darles a todos la misma porción de tiempo ...

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

Podría ser posible implementar una instalación de este tipo siempre que sus hilos estén preparados para cooperar. La clave es que los subprocesos se conviertan periódicamente en un programador personalizado y que el programador supervise el uso de la CPU de los subprocesos mediante JMX. Pero el problema es que si algún subproceso no hace que el programador llame a menudo, puede exceder los límites de regulación. Y no hay nada que puedas hacer con un hilo que se atasca en un bucle.

Otra ruta teórica para la implementación sería utilizar Aislados. Desafortunadamente, será difícil encontrar una JVM de propósito general que implemente aislamientos. Además, las API estándar solo le permiten controlar el aislamiento, no los subprocesos dentro del aislamiento.

La única manera de limitar el uso de la CPU de subprocesos es mediante el bloqueo de un recurso o para llamar a rendimiento () con frecuencia.

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

Para reducir la CPU, quieres dormir tus hilos dentro de los comunes if y mientras se mueven.

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

Dormir durante unos pocos 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 hacía algunos ajustes de rendimiento. Si se redujo la cantidad de memoria disponible para el subproceso, creo que es probable que exista una excepción de memoria o un subproceso muerto de hambre. Confiaría en que la JVM proporcione tanta memoria como el hilo necesario y trabajaría para reducir el uso de la memoria manteniendo solo objetos esenciales dentro del alcance en un momento dado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top