【问题标题】:Java MultiThread: unexpected execution timeJava MultiThread:意外的执行时间
【发布时间】:2018-01-08 23:21:30
【问题描述】:

我目前正在检查 Java 中的多线程行为,但得到了意想不到的结果。

这就是我正在做的事情:

  1. 生成 50 个随机邻接矩阵,大小为 600x600 和 将它们保存到 .txt 文件中
  2. 读取这些矩阵并使用 N 运行 Floyd Warshall 算法 没有同步的线程(从 1 到 3)。每个线程运行 包含 50 个矩阵的算法。

这是我得到的结果(N 个线程的总执行时间):

50 个 200x200 的矩阵:

  • 有 1 个线程:~483 毫秒
  • 有 2 个线程:~615 毫秒
  • 3 个线程:~741 毫秒

50 个 600x600 的矩阵:

  • 有 1 个线程:~9500 毫秒
  • 有 2 个线程:~15389 毫秒
  • 3 个线程:~16383 毫秒

50 个 1000x1000 的矩阵:

  • 有 1 个线程:~43140 毫秒
  • 有 2 个线程:~61408 毫秒
  • 3 个线程:~76219 毫秒

1、2 或 3 个线程的时间不应该大致相同吗? 为什么会增加?

这是运行 N 个线程的方法:

private static void runThreads(List<int[][]> graphs, int nThreads) throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(nThreads);
    Collection<Callable<String>> callables = new ArrayList<Callable<String>>();

    for(int i = 0; i < nThreads; i++) {
        callables.add(new Callable<String>() {
            @Override
            public String call() throws Exception {
                for(int[][] graph: graphs) {
                    FloydWarshall f = new FloydWarshall(graph);
                    f.checkConsistency();
                }
                return null;
            }
        });
    }

    long startTime = System.currentTimeMillis();

    List<Future<String>> futures = executor.invokeAll(callables);

    executor.shutdown();

    long endTime   = System.currentTimeMillis();
    long totalTime = endTime - startTime;
    System.out.println("Total time: " + totalTime  + " ms");        
}

【问题讨论】:

  • 也许 IO 是瓶颈?只是猜测。
  • 看起来你运行算法 n 次,其中 n 是线程数。所以更多的线程更多的时间。
  • @C-Otto 这也是我的猜测。这是JVM的限制还是什么?
  • @zhh 这些作为 Runnables 提交给执行者。理论上增加n 不会导致更长的执行时间,前提是硬件可以扩展。
  • 另一个建议是寻找正确的方法来进行基准测试,因为仅仅测量时间是不够的,你必须考虑 jvm 预热、jit 优化等等

标签: java multithreading


【解决方案1】:

正如@C-Otto 在 cmets 中所说,您在多个线程上多次运行该任务:

for(int i = 0; i < nThreads; i++) { // <-- n threads are created
    System.out.println("Running thread #" + (i+1));
    executor.submit(new Runnable(){
        public void run(){
             for(int[][] graph: graphs) { // <-- operating on the whole list
                  FloydWarshall f = new FloydWarshall(graph);
                  f.checkConsistency();
             }
        }
    });         
}

因此,您在查看代码时看到的行为与预期的一样。因为您只是在创建n 线程,它们都同时执行整个列表。执行时间的缺点来自 JVM 必须处理的额外负载(例如,创建一个新线程、执行整个算法等等)。

但您可能想要平衡n 线程上的负载,因此每个线程只处理整体的一小部分 (1/nth)。

因此您可能需要定义一些逻辑来拆分源(例如SpliteratorForkJoinPoolStreams),然后再次测量时间。

【讨论】:

  • 我知道我可以拆分矩阵列表并将每个不同的部分分配给一个线程。我想了解的是,如果每个线程都运行相同的代码,为什么总时间会增加。 I/O 瓶颈还是什么?
  • @JohnF 我假设该算法使用大量 CPU 来检查一致性,因此您可能遇到了硬件瓶颈
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-25
  • 2021-01-18
  • 2018-07-18
  • 2011-11-20
相关资源
最近更新 更多