Reputation: 9253
My application uses an Executor to provide a thread pool for a large number of tasks. I've determined, through both analysis and benchmarking, that my application runs fastest when there are multiple threads per core. A good heuristic is start 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, throttle the thread pool size based on CPU and RAM usage?
Failing that - How can I determine the number of cores programatically?
Upvotes: 5
Views: 4123
Reputation: 4357
One approach would be to use a ThreadPoolExecutor with a core size of 1, a starting maximum pool size of 4, then adjust the maximum pool size dynamically, based on memory and CPU usage.
The bigger problem, imho, is how to measure memory usage and CPU load. The memory usage is easy enough:
public double memUsageRatio() {
Runtime r = Runtime.getRuntime();
return (double) (r.totalMemory() - r.freeMemory()) / r.maxMemory();
}
For the CPU load, it can be more problematic, depending on the platform you run on. On Linux, you can use:
ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
This returns the system load average for the last minute. Unfortunately, on Windows this method always returns -1. In the past, I have replaced it with an approximation of the system load average for a given interval, by computing the sum of the CPU times for all the threads, divided by the sum of all elapsed times for all processors. This is only an approximation, but it works pretty good 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;
}
}
Upvotes: 2
Reputation: 198211
Runtime.availableProcessors()
Javadoc:
Returns the number of processors available to the Java virtual machine. This value may change during a particular invocation of the virtual machine. Applications that are sensitive to the number of available processors should therefore occasionally poll this property and adjust their resource usage appropriately.
Upvotes: 2