I am trying to implement Factorial function via divide and conquer strategy. I used the ForkJoin framework to fork each recursive task to speed up the computation.
But I found that its not speeding up as i expected.
It took 28 seconds to calculate the factorial of 50000 without using the ForkJoin, while
it took 25 seconds when I used ForkJoin.
this is the code without forkjoin:
public static BigInteger factorial(long p, long q) {
if (q < p) {
return new BigInteger("1");
}
if (p == q) {
return new BigInteger("" + p);
}
BigInteger fact = new BigInteger("1");
fact = fact.multiply(factorial(p, (p + q) / 2)).multiply(factorial((p + q) / 2 + 1, q));
return fact;
}
And this is the code with forkJoin:
public class Factorial extends RecursiveTask<BigInteger>{
private long p, q;
public Factorial(long p, long q) {
this.p = p;
this.q = q;
}
@Override
public BigInteger compute() {
if(q < p) {
return new BigInteger("1");
}
if( p == q) {
return new BigInteger(""+p);
}
Factorial f1 = new Factorial(p, (p+q)/2);
Factorial f2 = new Factorial((p+q)/2 + 1, q);
f2.fork();
return f1.compute().multiply(f2.join());
}
}
Where am I going wrong? I don’t think this would be the result of Fork/Join. Please Help!
Fork/Join can paralellize computing. It is: gives you a constant gain (divide the time by 4 by example). And that’s bounded by the real CPUs you have (200 threads will only share the same 4 CPUs by example).
In contrast factorial (the typical algorithm) is
O(N!)what it means it grows very very very fast.And if you create a new fork for each step the overhead of forking and joining compensates the gain from parallelizing.
So the important thing is trying to calculate factorial with another algorith that’s not
O(N!). If you apply dynamic programming (resusing intermediate results), you can convert it intoO(N).I don’t know the algorithm you are trying to imitate by what I should do is mantaining a matrix or something with calculations for pairs in order to reuse them when I need them the second time…
Looking at your code I can see each factorial method execution provokes two child executions:
(p+q)/2,qandp,(p+q)/2+1… or something like that. If each time factorial method finds a result, saves it in a map[Pair -> BigDecimal], you can query this cache at the beginning of the method. Make this map a member of your class (or pass it through arguments) so different method invocations share the map.