对于在循环中重复执行多个相同计算任务的应用程序,ExecutorService是否是在开销方面在CPU之间分配任务的最合适的解决方案?
下面是我使用此答案https://stackoverflow.com/a/28632378/2721750构建的测试应用程序,以确定在2个英特尔酷睿i5 2.5 GHz物理内核上并行运行的2个任务,每个周期的开销约为0.1毫秒。
是否有其他解决方案可以帮助减少开销?
要求是任务需要接收参数并从/向主线程返回值。它们是对我无法改变的专有API的调用。
package asyncthreadbenchmark;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AsyncThreadBenchmark {
public static Double CalcResult(Integer p)
{
Long startTime = System.nanoTime();
double d = 0;
/* Simulating payload for the given number of milliseconds */
while ((System.nanoTime() - startTime) / 1000000 < p)
{
d = Math.PI * Math.pow(Math.log((double)p), Math.E);
}
return d;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
class CalcTask implements Callable<Double> {
Integer p;
CalcTask(Integer parameter) {
p = parameter;
}
@Override
public Double call() {
return CalcResult(p);
}
}
/*
Using Intel Core i5 2.5 GHz dual core, execuction of 10,000 cycles
with 6 ms per task max resulted in 61024-61034 ms total.
That is 0.1 ms overhead for 2 tasks per cycle.
If the savings from running 2 tasks in parallel exceed 0.1 ms
it makes sense to use Executor Service. Otherwise...
*/
ExecutorService executor = Executors.newFixedThreadPool(3);
try {
Integer param1, param2, param3;
param1 = 5;
param2 = 6;
// param3 = 4;
Future aFuture, bFuture, cFuture;
Double a, b, c;
Long startTime = System.nanoTime(), elapsed;
for (long i = 0; i< 10000; i++)
{
aFuture = executor.submit(new CalcTask(param1));
bFuture = executor.submit(new CalcTask(param2));
// cFuture = executor.submit(new CalcTask(param3));
try {
a = (Double)aFuture.get();
b = (Double)bFuture.get();
// c = (Double)cFuture.get();
} catch (InterruptedException | ExecutionException ex) {
// Logger.getLogger(AsyncThreadBenchmark.class.getName()).log(Level.SEVERE, null, ex);
}
}
elapsed = (System.nanoTime() - startTime) / 1000000;
System.out.println("Elapsed " + Long.toString(elapsed) + "ms");
}
finally {
executor.shutdown();
}
}
}
答案 0 :(得分:1)
这取决于你想要做什么。
ExecutorService
是执行独立,中等粒度任务的首选工具,具有自然的任务粒度(例如,为用户请求提供服务。)
相反,如果要执行细粒度协作任务,例如可并行化问题的递归分解所产生的任务,您可能需要ForkJoinPool
。
Java 8中的并行流使用ForkJoinPool
来分解和执行流操作。如果您还不是并行专家,那么最好从并行流开始。