Java ForkJoinPool不使用所有CPU内核

时间:2017-05-02 17:08:51

标签: java fork-join forkjoinpool

我在虚拟墙的节点上运行以下代码。该节点具有32个Intel Xeon E7 / E5内核和128GB RAM。监视CPU使用情况表明该节点远未完全运行。由于节点大小,此问题与大多数fork-join问题不同。有时节点在多个内核上有20%以上的CPU负载,显示出并行性的迹象,但我似乎无法使其使用更多资源。

给出一些背景;问题是111个节点(Parcs / parken)图中的最大化问题。每个公园内都隐藏着许多鸡蛋。这个数字每过一秒便呈指数下降。目标是在时间到期之前获得尽可能多的鸡蛋。 ' OPL'我使用贪婪算法找到了一个解决方案,所以为了缩小递归树,我只允许递归,因为我们发现最多5个鸡蛋比我们同时找到的贪婪算法少。 / p>

我熟悉(多线程)线程,但远非专家。我之前没有使用过很多ForkJoinPools。我也尝试将ForkJoinPool参数操作为16/32,但没有成功。

enter image description here

主:

Algoritmes.AlgoritmeRecursive.run(new AlgoritmeRecursive(parken, tabel, opl, 22, 1000, 0, 0)));

类别:

public static class AlgoritmeRecursive extends RecursiveTask<Double> {
    private ArrayList<Park> parken = new ArrayList<Park>();
    private double[][] afstandenTabel;
    private double[][] oplossing;
    private int startpark;
    private double duur;
    private double eieren;
    private int time;

    AlgoritmeRecursive(ArrayList<Park> parken, double[][] afstandenTabel, double[][] oplossing, int startpark, double duur, double eieren, int time) {
        for (Park p : parken) {
            this.parken.add(new Park(p));
        }
        this.afstandenTabel = afstandenTabel;
        this.oplossing = oplossing;
        this.startpark = startpark;
        this.duur = duur;
        this.eieren = eieren;
        this.time = time;
    }

    public static double run(AlgoritmeRecursive ar) {
        ForkJoinPool pool= new ForkJoinPool();
        return pool.invoke(ar);
    }

    protected Double compute() {
        if (duur < 1.0) return eieren;

        double gevonden = 0;

        /* startpark zoeken adhv gegeven naam */
        for (Park p : parken) {
            if (p.getId() == startpark) {
                gevonden = p.verwachtAantalEieren(40, 0);
                p.updateEggs(p.getEggs() * exp((-1.0/10800.0) * ((p.getStartEggs()/20.0) + 40.0)));
            }
            else {
                p.updateEggs(p.getEggs() * exp((-1.0/10800.0) * (p.getStartEggs()/20.0)));
            }
        }
        double score = eieren;
        for (Park p : parken) {
            if (p.getId() == startpark && eieren >= (oplossing[1000-(int)duur][1] - 5)) {
                AlgoritmeRecursive ar = new AlgoritmeRecursive(parken, afstandenTabel, oplossing, startpark, duur-1, eieren + gevonden, time+1);
                ar.fork();
                double res = ar.join();
                if(res > score) score = res;
            }
            else if (duur-afstandenTabel[startpark][p.getId()] > 60.0 && time > 120.0 && eieren >= oplossing[1000-(int)duur][1] && gevonden < p.verwachtAantalEieren(40,afstandenTabel[startpark][p.getId()])){
                AlgoritmeRecursive ar = new AlgoritmeRecursive(parken, afstandenTabel, oplossing, p.getId(), duur-afstandenTabel[startpark][p.getId()], eieren, 0);
                for (Park p2 : ar.parken) {
                    p2.updateEggs(p2.getEggs() * exp((-1.0/10800.0) * (p2.getStartEggs()/20.0) * (afstandenTabel[startpark][p.getId()]-1)));
                }
                ar.fork();
                double res = ar.join();
                if(res > score) score = res;
            }
        }
        return score;
    }
    public double exp(double x) {
          x = 1d + x / 256d;
          x *= x; x *= x; x *= x; x *= x;
          x *= x; x *= x; x *= x; x *= x;
          return x;
    }
}

2 个答案:

答案 0 :(得分:0)

我自己并不是很熟悉,但是对ar.join()的调用是否会使你的RecursiveTask等到子任务完成之后?如果是这种情况,您的其他任务将不会在上一个任务完成之前启动。

您可以尝试将正在运行的任务存储在列表中,然后再加入它们。希望确保所有子任务在等待之前开始运行。

这样的事情(在compute中修改你的第二个循环):

List<AlgoritmeRecursive> tasks = new ArrayList<>();

for (Park p : parken) {
    if (p.getId() == startpark && eieren >= (oplossing[1000-(int)duur][1] - 5)) {

        AlgoritmeRecursive ar = new AlgoritmeRecursive(parken, afstandenTabel, oplossing, startpark, duur-1, eieren + gevonden, time+1);
        ar.fork();
        tasks.add(ar); // Adding the running task to the list.

    } else if (duur-afstandenTabel[startpark][p.getId()] > 60.0 && time > 120.0 && eieren >= oplossing[1000-(int)duur][1] && gevonden < p.verwachtAantalEieren(40,afstandenTabel[startpark][p.getId()])){

        AlgoritmeRecursive ar = new AlgoritmeRecursive(parken, afstandenTabel, oplossing, p.getId(), duur-afstandenTabel[startpark][p.getId()], eieren, 0);
        for (Park p2 : ar.parken) {
            p2.updateEggs(p2.getEggs() * exp((-1.0/10800.0) * (p2.getStartEggs()/20.0) * (afstandenTabel[startpark][p.getId()]-1)));
        }
        ar.fork();
        tasks.add(ar); // Adding the running task to the list.

    }
}

double score = eieren;
for(AlgoritmeRecursive task : tasks) {
    double res = ar.join();
    if(res > score) score = res;
}

return score;

答案 1 :(得分:0)

我认为问题在于算法的递归部分是这样的:

    for (...) {
        // ar <- create sub-problem
        ar.fork();
        double res = ar.join();
        // Use result
    }

问题是当你分叉然后立即加入时,没有两个或多个子问题并行运行的余地。这与使用经典线程执行此操作相同:

    Thread t = new Thread(someRunnable);
    t.start();
    t.join();

启动一个新线程,并立即阻止当前线程,直到新线程结束;即有效地单线程。这样做效率更高:

    someRunnable.run();

尝试在一个循环中进行分叉,然后在另一个循环中进行分组。