Java奇怪的性能不一致性

Java奇怪的性能不一致性,java,performance,jvm,Java,Performance,Jvm,我有一个简单的递归方法,深度优先搜索。每次调用时,它都会检查它是否在叶中,否则它会展开当前节点并在子节点上调用自己 我试图使它平行,但我注意到以下对我来说很奇怪的问题 我使用System.currentTimeMillis测量执行时间 当我将搜索分解为若干个子搜索并加上总执行时间时,得到的数字比顺序搜索要大。我只测量执行时间,没有通信或同步等。当我添加子任务的时间时,我希望得到相同的时间。即使我只是一个接一个地运行任务,也会发生这种情况,因此没有线程。如果我只是把搜索分成几个子任务,然后一个接一

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

我试图使它平行,但我注意到以下对我来说很奇怪的问题

我使用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);
}
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;
    }
}
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);
    }

}
每当我叫它时,我都是这样做的:

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);
}
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;
    }
}
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);
    }

}

问题是总时间随着num_任务的增加而增加,我希望保持不变,因为method_调用的变量保持不变。

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


我认为,如果你扩大研究树,你将受益于并行化。

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

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

可能发生的情况是,至少在部分时间内,准备执行的线程比系统具有cpu内核的线程要多,并且调度程序会使其中一个线程处于睡眠状态,这导致它需要更长的时间才能完成。使用的线程越多,这种影响就越严重,这是有道理的。即使你的程序使用的线程比你的内核少,其他程序,比如你的开发环境。。。可能


如果您对CPU使用感兴趣,您可能希望查询我希望看到使用的线程。大概是这样的:

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);
}
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;
    }
}
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);
    }

}

现在还不清楚你在做什么,但是如果你能发布完整的代码,那会很有帮助。创建线程不是免费的吗?预先创建线程并使用池或@user16367-上下文切换不是免费的吗?如上所述,没有代码是不可能的,但即使这样,考虑到每个任务的平均差异高达200ms/13,也很难做到这一点。只需再调用System.currentTimeMillis 12次就会产生一些开销。@user16367:您已经添加了一些代码,但是如果您可以发布一个简短但完整的程序来显示这两种方法,这会有所帮助,这样我们就可以自己进行测量。这里没有任何迹象表明您正在并行执行任何操作。对dfs的调用在执行下一个任务之前完成每个任务。你是如何使用线程的?但我在这两种情况下都有相同数量的方法调用。@Brian Roach所说的将在程序初始化或创建池时转移开销。如果你必须加快研究,你没有问题等待初始化它ok!如果你能更多地重复使用游泳池也很好!对不起,我肯定会写关于线程的文章,但我以前没有写过!:做一个平均值确实有帮助,但不知道为什么。必须是代码中的其他内容,这些内容会随着多次运行而摊销。