4

我的程序使用如下所示的 fork/join 来运行数千个任务:

private static class Generator extends RecursiveTask<Long> {
    final MyHelper mol;
    final static SatChecker satCheck = new SatChecker();

    public Generator(final MyHelper mol) {
        super();
        this.mol = mol;
    }

    @Override
    protected Long compute() {
        long count = 0;
        try {
            if (mol.isComplete(satCheck)) {
                count = 1;
            }
            ArrayList<MyHelper> molList = mol.extend();
            List<Generator> tasks = new ArrayList<>();
            for (final MyHelper child : molList) {
                tasks.add(new Generator(child)); 
            }
            for(final Generator task : invokeAll(tasks)) { 
                count += task.join(); 
            }
        } catch (Exception e){
            e.printStackTrace();
        }       
        return count;           
    }
}

我的程序大量使用第三方库来实现 isComplete 和 extend 方法。extend 方法也使用了本地库。就 MyHelper 类而言,任务之间没有共享变量或同步。

我使用来自 linux 的 taskset 命令来限制我的应用程序使用的内核数量。我通过使用大约 10 个内核(比如大约 60 秒)获得了最佳速度。这意味着使用超过 10 个内核会导致应用程序变慢,因此 16 个内核与 6 个内核在同一时间内完成(大约 90 秒)。

我更困惑,因为选定的核心 100% 忙(不时进行垃圾收集除外)。有谁知道什么会导致这样的减速?我应该在哪里解决这个问题?

PS:我也在 Scala/akka 和使用 ThreadPoolExecutor 中进行了实现,但结果相似(虽然比 fork/join 慢)

PPS:我的猜测是,在 MyHelper 或 SatCheck 的深处,有人越过了内存屏障(中毒缓存)。但是我怎样才能找到并修复或解决它呢?

4

2 回答 2

1

由于将线程/任务分配给不同的内核,可能会出现过载。另外,您确定您的程序是完全可并行化的吗?事实上,一些程序不能总是 100% 有效地使用所有可用的 cpu,并且调度任务所花费的时间可能会减慢程序的速度,而不是帮助它。

于 2012-07-26T17:10:32.670 回答
0

我认为您应该对molList(或mol)变量的大小使用阈值,以避免对太小的数据集进行分叉。

我一直在使用 fork/join 来了解框架,而我的第一个示例没有考虑到阈值。显然我的表现很糟糕。解决问题大小的适当限制就可以了。

为阈值找到正确的值需要您花一些时间尝试不同的值并查看性能如何变化。

因此,if在方法的最开始放置一个,compute如下所示:

@Override
protected Long compute() {
    if (mol.getSize() < THRESHOLD) //getSize or whatever gives you size of problem
         return noForkJoinCompute(mol); //noForkJoinCompute gives you count without FJ

    long count = 0;
    try {
        if (mol.isComplete(satCheck)) {
            count = 1;
        }
    ...
于 2013-02-03T22:15:09.073 回答