Java executor that configures thread pool based on CPU and RAM usage

My application is using an Executor to provide a thread pool for a large number of tasks. I have determined through analysis and benchmarking that my application runs fastest when there are multiple threads per core. A good heuristic starts with 4 threads per core, varying until you hit> 90% CPU or> 90% RAM.

Is there an Executor available that will do this out of the box? Either automatically use N threads per core (not just one), or ideally reduce the size of the thread pool based on CPU and RAM usage?

Failed - How to determine the number of cores programmatically?

+3


source to share


2 answers


One approach would be to use a ThreadPoolExecutor with a core size of 1, an initial maximum pool size of 4, then dynamically adjust the maximum pool size based on memory and CPU usage.

The big problem imho is how to measure memory usage and cpu load. Memory usage is simple enough:

public double memUsageRatio() {
  Runtime r = Runtime.getRuntime();
  return (double) (r.totalMemory() - r.freeMemory()) / r.maxMemory();
}

      

For CPU usage, this can be more problematic, depending on the platform you are running on. On Linux, you can use:



ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();

      

This returns the average system load over the last minute. Unfortunately on Windows this method always returns -1. Previously, I replaced it with an approximation of the average system load for a given interval, calculating the sum of the processor times for all threads, divided by the sum of all elapsed times for all processors. This is only an approximation, but it works very well in most cases:

import java.lang.management.*;

public class CPUUsageCollector implements Runnable {
  private final static long INTERVAL = 1000L; // polling interval in ms
  private long totalCpuTime = 0L; // total CPU time in millis
  private double load = 0d; // average load over the interval
  ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
  boolean stopped = false;

  @Override
  public void run() {
    try {
      while (!isStopped()) {
        long start = System.currentTimeMillis();
        long[] ids = threadMXBean.getAllThreadIds();
        long time = 0L;
        for (long id: ids) {
          long l = threadMXBean.getThreadCpuTime(id);
          if (l >= 0L) time += l;
        }
        long newCpuTime = time / 1000000L;
        synchronized(this) {
          long oldCpuTime = totalCpuTime;
          totalCpuTime = newCpuTime;
          // load = CPU time difference / sum of elapsed time for all CPUs
          load = (double) (newCpuTime - oldCpuTime) / 
           (double) (INTERVAL * Runtime.getRuntime().availableProcessors());
        }
        long sleepTime = INTERVAL - (System.currentTimeMillis() - start);
        goToSleep(sleepTime <= 0L ? INTERVAL : sleepTime);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public synchronized double getLoad() {
    return load;
  }

  public synchronized void goToSleep(final long time) {
    try {
      wait(time);
    } catch(InterruptedException e) {
      e.printStackTrace();
    }
  }

  public synchronized boolean isStopped() {
    return stopped;
  }

  public synchronized void setStopped(final boolean stopped) {
    this.stopped = stopped;
  }
}

      

+2


source


Runtime.availableProcessors()

      

Javadoc:



Returns the number of processors available for the Java virtual machine. This value can change during a specific invocation of the virtual machine. Therefore, applications that are sensitive to the number of available processors should periodically test this property and adjust their resource usage accordingly.

+1


source







All Articles