Parallel Fibonacci Number Calculator

心已入冬 提交于 2019-11-28 10:56:48

问题


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();
            Console.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?


回答1:


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.




回答2:


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.




回答3:


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));
    }
}



回答4:


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.



来源:https://stackoverflow.com/questions/7672543/parallel-fibonacci-number-calculator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!