我有一个简单的递归方法,深度优先搜索。在每次调用时,它会检查它是否在一个叶子中,否则它会扩展当前节点并在子节点上调用它自己。
我试图让它平行,但我注意到以下奇怪的(对我来说)问题。
我使用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变量保持不变。
答案 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);
}
}