我的程序使用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方法也使用本机库。就MyHelper类而言,任务之间没有共享变量或同步。
我使用linux中的taskset命令来限制我的应用程序使用的核心数。我通过使用大约10个核心(比如大约60秒)获得最佳速度。这意味着使用10个以上的内核会导致应用程序变慢,因此16个内核可以同时完成6个内核(大约90秒)。
我更困惑,因为所选内核100%忙碌(除了垃圾收集之外)。 有谁知道什么会导致这么慢?我应该在哪里解决这个问题?
PS:我在Scala / akka中也使用了ThreadPoolExecutor,但结果相似(虽然比fork / join慢)
PPS:我的猜测是,在MyHelper或SatCheck的深处,有人穿过内存屏障(中毒缓存)。但我怎样才能找到并修复或解决它呢?
答案 0 :(得分:1)
由于将线程/任务分配给不同的核心,可能会出现过载。此外,您确定您的程序完全可并行化吗?实际上,某些程序不能总是100%有效地使用所有可用的cpu,并且分配任务所花费的时间可能会使程序减慢而不是帮助它。
答案 1 :(得分: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;
}
...