为什么在大约10个核心之后添加核心会减慢我的java程序的速度



我的程序使用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%繁忙的(除了偶尔的垃圾收集)。有人知道是什么导致了这样的减速吗?我应该在哪里解决这个问题?

附言:我也在Scala/akka中使用ThreadPoolExecutor进行了实现,但结果相似(尽管比fork/join慢)

PPS:我的猜测是,在MyHelper或SatCheck的深处,有人越过了内存屏障(毒害了缓存)。但我该如何找到它并修复或着手解决它呢?

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

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

我一直在玩fork/join,只是为了理解框架,我的第一个例子并没有考虑阈值。很明显,我的表现很糟糕。修正问题大小的适当限制就成功了。

为阈值找到合适的值需要花费一些时间尝试不同的值,并查看性能如何变化。

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

@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;
        }
    ...

最新更新