Parallel Fibonacci Number Calculator
I'm using Task Parallel Library (TPL ) for calculating Fibonacci number. Program is given below:
public static int Fib(int n)
{
if (n <= 1)
{
return n;
}
Task<int> task = Task.Factory.StartNew<int>(() => Fib(n - 1));
var p = Fib(n - 2);
return task.Result + p;
}
public static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Console.WriteLine("Answer: " + Fib(44));
watch.Stop();
Console.WriteLine("Time: " + watch.ElapsedMilliseconds);
}
}
Unfortunately this program takes a very long time to complete. But serial version of this program ( as given below ) takes less than 30 seconds to calculate 44th Fibonacci number.
public class FibTester
{
public static int Fib(int n)
{
if (n <= 1)
{
return n;
}
var q = Fib(n - 1);
var p = Fib(n - 2);
return p + q;
}
public static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Co开发者_JAVA百科nsole.WriteLine("Answer: " + Fib(44));
watch.Stop();
Console.WriteLine("Time: " + watch.ElapsedMilliseconds);
}
}
I think issue in parallel version is, it creates a thread for each Fib(n - 1)
request. Is there any way to control number of thread created in TPL?
This is a perfect example of how not to multithread!
You are creating a new task for each iteration of a recursive function. So each task creates a new task, waits for that task to finish and then adds the numbers from the result.
Each thread has two jobs : 1 - to create a new thread, 2 - to add two numbers.
The overhead cost for creating each thread is going to far outweigh the cost of adding two numbers together.
To answer your question about limiting the number of threads created, the TPL uses the ThreadPool. You can limit the number of threads using ThreadPool.SetMaxThreads.
I think it is pretty clear that fibonacci cannot be parallelized unless you know some pairs of adjacent fibonacci numbers ahead of time
Just go for the iterative code.
Whatever you do, don't spawn a Task/Thread on each iteration/recursion! The overhead will kill the performance. That is a big fat Anti Pattern even if parallellization applies.
Just for fun :)
using System;
using System.Linq;
using System.Threading.Tasks;
public class Program
{
static readonly double sqrt5 = Math.Sqrt(5);
static readonly double p1 = (1 + sqrt5) / 2;
static readonly double p2 = -1 * (p1 - 1);
static ulong Fib1(int n) // surprisingly slightly slower than Fib2
{
double n1 = Math.Pow(p1, n+1);
double n2 = Math.Pow(p2, n+1);
return (ulong)((n1-n2)/sqrt5);
}
static ulong Fib2(int n) // 40x faster than Fib3
{
double n1 = 1.0;
double n2 = 1.0;
for (int i=0; i<n+1; i++)
{
n1*=p1;
n2*=p2;
}
return (ulong)((n1-n2)/sqrt5);
}
static ulong Fib3(int n) // that's fast! Done in 1.32s
{
double n1 = 1.0;
double n2 = 1.0;
Parallel.For(0,n+1,(x)=> {
n1 *= p1;
n2 *= p2;
});
return (ulong)((n1-n2)/sqrt5);
}
public static void Main(string[] args)
{
for (int j=0; j<100000; j++)
for (int i=0; i<90; i++)
Fib1(i);
for (int i=0; i<90; i++)
Console.WriteLine(Fib1(i));
}
}
Your program is very inneficient, because same calculation are repeated (Fib(n-1) actually recalculate the Fib number for all numbers < n -2, which has be done yet).
You should try this :
class Program
{
static void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
foreach (var nbr in Fibo().Take(5000))
{
Console.Write(nbr.ToString() + " ");
}
sw.Stop();
Console.WriteLine();
Console.WriteLine("Ellapsed : " + sw.Elapsed.ToString());
Console.ReadLine();
}
static IEnumerable<long> Fibo()
{
long a = 0;
long b = 1;
long t;
while (true)
{
t = a + b;
yield return t;
a = b;
b = t;
}
}
}
44th find in 5ms.
The slowest part of the code is the Console.Write in the loop.
精彩评论