Java奇怪的性能不一致

时间:2012-02-04 14:25:20

标签: java performance jvm

我有一个简单的递归方法,深度优先搜索。在每次调用时,它会检查它是否在一个叶子中,否则它会扩展当前节点并在子节点上调用它自己。

我试图让它平行,但我注意到以下奇怪的(对我来说)问题。

我使用System.currentTimeMillis()测量执行时间。

当我将搜索分解为多个子搜索并添加总执行时间时,我得到的数字比顺序搜索的数字大。我只测量执行时间,没有通信或同步等。当我添加子任务的时间时,我希望得到相同的时间。即使我只是在另一个之后运行一个任务,也会发生这种情况,因此没有线程。如果我只是将搜索分解为一些子任务并一个接一个地运行子任务,我会得到更长的时间。 如果我为子任务添加方法调用的数量,我得到与顺序搜索相同的数字。所以,基本上,在这两种情况下,我都会进行相同数量的方法调用,但是我会得到不同的时间。

我猜测初始方法调用或JVM机制引起的其他问题会产生一些开销。任何想法可能是什么? 例如,一次顺序搜索大约需要3300毫秒。如果我把它分成13个任务,总共需要3500毫秒。

我的方法如下:

private static final int dfs(State state) {
    method_calls++;
    if(state.isLeaf()){
            return 1;
    }
    State[] children = state.expand();
    int result = 0;
    for (int i = 0; i < children.length; i++) {
            result += dfs(children[i]);
    }
    return result;
}

每当我打电话给我时,我就这样做:

for(int i = 0; i < num_tasks; i++){
    long start = System.currentTimeMillis();
    dfs(tasks[i]);
    totalTime += (System.currentTimeMillis() - start);
}

问题是totalTime随着num_tasks的增加而增加,我希望保持不变,因为method_calls变量保持不变。

4 个答案:

答案 0 :(得分:0)

与所有编程语言一样,无论何时调用过程或方法,都必须推送环境,初始化新程序,执行程序指令,返回堆栈上的值,最后重置先前的环境。它花了一点钱!另外创建一个线程成本!

我想如果你放大研究树,你将会受益于并行化。

答案 1 :(得分:0)

您应该在较长时间内平均数字。其次currentTimeMillis的精度可能不够,你可以尝试使用System.nanoTime()。

答案 2 :(得分:0)

为多个线程添加系统时钟时间似乎是个奇怪的想法。您要么对处理完成之前的时间感兴趣,在这种情况下添加没有意义,或者在cpu使用中,在这种情况下,您应该只计算线程实际安排执行的时间。

可能发生的情况是,至少有部分时间,比系统具有cpu核心更多线程准备好执行,并且调度程序将其中一个线程置于休眠状态,这导致需要更长时间才能完成。有意义的是,您使用的线程越多,这种影响就会恶化。 (即使您的程序使用的线程少于核心,其他程序(例如您的开发环境......)也可能。)

如果您对CPU使用率感兴趣,可以查询ThreadMXBean.getCurrentThreadCpuTime

答案 3 :(得分:0)

我希望看到使用Threads。像这样:

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class Puzzle {

    static volatile long totalTime = 0;
    private static int method_calls = 0;

    /**
     * @param args
     */
    public static void main(String[] args) {
       final int num_tasks = 13;
       final State[] tasks = new State[num_tasks];
       ExecutorService threadPool = Executors.newFixedThreadPool(5);
       for(int i = 0; i < num_tasks; i++){
           threadPool.submit(new DfsRunner(tasks[i]));
       }
       try {
         threadPool.shutdown();
         threadPool.awaitTermination(1, TimeUnit.SECONDS);
       } catch (InterruptedException e) {
           System.out.println("Interrupted");
   }
       System.out.println(method_calls + " Methods in " + totalTime + "msecs");
    }

    static final int dfs(State state) {
        method_calls++;
        if(state.isLeaf()){
                return 1;
        }
        State[] children = state.expand();
        int result = 0;
        for (int i = 0; i < children.length; i++) {
                result += dfs(children[i]);
        }
        return result;
    }
}

使用这样的runnable位:

public class DfsRunner implements Runnable {
    private State state;
    public DfsRunner(State state) {
       super();
       this.state = state;
    }
    @Override
    public void run() {
        long start = System.currentTimeMillis();
        Puzzle.dfs(state);
        Puzzle.totalTime += (System.currentTimeMillis() - start);
    }

}