我有一些练习,其中一个是关于并发的。这个主题对我来说是新的,但是我花了 6 个小时终于解决了我的问题。但是我对相应API的了解很差,所以我需要建议:我的解决方案是否正确,或者是否有更合适的方法。
所以,我必须实现下一个接口(interface):
public interface PerformanceTester {
/**
* Runs a performance test of the given task.
* @param task which task to do performance tests on
* @param executionCount how many times the task should be executed in total
* @param threadPoolSize how many threads to use
*/
public PerformanceTestResult runPerformanceTest(
Runnable task,
int executionCount,
int threadPoolSize) throws InterruptedException;
}
其中PerformanceTestResult包含总时间(整个性能测试总共花费了多长时间)、最短时间(最短的单次执行所花费的时间)和最长时间(最长的单次执行所花费的时间) .
所以,我今天学到了很多新东西——关于线程池、类型 Executors
, ExecutorService
, Future
, CompletionService
等
如果我有 Callable task
,我可以做下一个:
- 在
call()
结束时返回当前时间程序。 - 创建一些数据结构(可能是一些 Map)来存储开始时间和
Future
对象,由fixedThreadPool.submit(task)
重新调整(循环执行executionCount
次); - 执行后,我可以将每个
Future
的结束时间减去开始时间.
(对于可调用任务,这是正确的方法吗?)
但是!我只有 Runnable task
,所以我继续寻找。我什至创造了FutureListener implements Callable<Long>
,必须返回时间,当Future.isDone()
,但对我来说接缝有点疯狂(我必须双线程计数)。
所以,最终我注意到了CompletionService
用有趣的方法打字 take()
,检索并删除代表下一个已完成任务的 Future,如果还没有任务则等待。,以及使用 ExecutorCompletionService 的非常好的示例.这是我的解决方案。
public class PerformanceTesterImpl implements PerformanceTester {
@Override
public PerformanceTestResult runPerformanceTest(Runnable task,
int executionCount, int threadPoolSize) throws InterruptedException {
long totalTime = 0;
long[] times = new long[executionCount];
ExecutorService pool = Executors.newFixedThreadPool(threadPoolSize);
//create list of executionCount tasks
ArrayList<Runnable> solvers = new ArrayList<Runnable>();
for (int i = 0; i < executionCount; i++) {
solvers.add(task);
}
CompletionService<Long> ecs = new ExecutorCompletionService<Long>(pool);
//submit tasks and save time of execution start
for (Runnable s : solvers)
ecs.submit(s, System.currentTimeMillis());
//take Futures one by one in order of completing
for (int i = 0; i < executionCount; ++i) {
long r = 0;
try {
//this is saved time of execution start
r = ecs.take().get();
} catch (ExecutionException e) {
e.printStackTrace();
return null;
}
//put into array difference between current time and start time
times[i] = System.currentTimeMillis() - r;
//calculate sum in array
totalTime += times[i];
}
pool.shutdown();
//sort array to define min and max
Arrays.sort(times);
PerformanceTestResult performanceTestResult = new PerformanceTestResult(
totalTime, times[0], times[executionCount - 1]);
return performanceTestResult;
}
}
那么,你能说什么呢?感谢您的回复。
最佳答案
我会使用 System.nanoTime() 来获得更高分辨率的计时。您可能希望忽略前 10,000 个测试以确保 JVM 已经预热。
我不会费心创建一个 Runnable 列表并将其添加到执行器中。我只想将它们添加到执行程序中。
使用 Runnable 不是问题,因为您会得到一个 Future<?>
回来。
注意:计算任务在队列中花费的时间可能会对时间产生很大影响。无需从创建任务时开始计时,您可以让任务计时并返回一个 Long 以纳秒为单位的时间。计时的完成方式应反射(reflect)您所考虑的用例。
一种将 Runnable 任务转换为计时任务的简单方法。
finla Runnable run = ...
ecs.submit(new Callable<Long>() {
public Long call() {
long start = System.nanoTime();
run.run();
return System.nanoTime() - start;
}
});
关于java - 如何使用多线程测试任务性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15304709/