java - 如何使用多线程测试任务性能?

标签 java multithreading performance concurrency java.util.concurrent

我有一些练习,其中一个是关于并发的。这个主题对我来说是新的,但是我花了 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 ,我可以做下一个:

  1. call()结束时返回当前时间程序。
  2. 创建一些数据结构(可能是一些 Map)来存储开始时间和 Future对象,由 fixedThreadPool.submit(task) 重新调整(循环执行 executionCount 次);
  3. 执行后,我可以将每个 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/

相关文章:

java - "GC overhead limit exceeded"是失败的次要原因吗?

单例设计模式中的Java静态对象范围不会抛出空指针异常

java.security.SignatureSpi 用法

python - 在 Heroku 上使用线程更新数据库?

C++ 专用互斥锁访问

java - 防止transformer.transform( source, result ) 转义特殊字符

c# - 使用 SoundPlayer() 播放多个 wav 文件而不重叠声音

sql - 更新大型表上的行的最高效方法

php - 按州组织自定义 WordPress 帖子的更有效方法

如果在调试配置中编译 C++ hash_map.clear() 很慢