使用TPL加速pi的计算

时间:2014-09-19 08:53:29

标签: c# parallel-processing task-parallel-library

我需要使用任务并行库通过Monte-Carlo方法计算Pi数,但是当我的并行程序运行时,它计算的Pi数比它的不平行模拟长得多。如何修复它?并行计算类和它的不平行模拟如下:

class CalcPiTPL
    {
        Object randLock = new object();
        int n;
        int N_0;
        double aPi;
        public StringBuilder Msg; // diagonstic message
        double x, y;
        Stopwatch stopWatch = new Stopwatch();

        public void Init(int aN)
        {
            stopWatch.Start();
            n = aN; // save total calculate-iterations amount
            aPi = -1; // flag, if no any calculate-iteration has been completed
            Msg = new StringBuilder("No any calculate-iteration has been completed");
        }
        public void Run()
        {
            if (n < 1)
            {
                Msg = new StringBuilder("Inbalid N-value");
                return;
            }

            Random rnd = new Random(); // to create randomizer
            Task[] tasks = new Task[4];
            tasks[0] = Task.Factory.StartNew(() => PointGenerator(n, rnd));
            tasks[1] = Task.Factory.StartNew(() => PointGenerator(n, rnd));
            tasks[2] = Task.Factory.StartNew(() => PointGenerator(n, rnd));
            tasks[3] = Task.Factory.StartNew(() => PointGenerator(n, rnd));
            Task.WaitAll(tasks[0], tasks[1], tasks[2], tasks[3]);

            aPi = 4.0 * ((double)N_0 / (double)n); // to calculate approximate Pi - value
            stopWatch.Stop();
            TimeSpan ts = stopWatch.Elapsed;
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds / 10);
            Console.WriteLine("RunTime " + elapsedTime);
        }
        public double Done()
        {
            if (aPi > 0)
            {
                Msg = new StringBuilder("Calculates has been completed successful");
                return aPi; // return gotten value
            }
            else
            {
                return 0; // no result
            }
        }
        public void PointGenerator(int n, Random rnd)
        {
            for (int i = 1; i <= n / 4; i++)
            {
                lock (randLock)
                {
                    x = rnd.NextDouble(); // to generate coordinates
                    y = rnd.NextDouble(); // 

                    if (((x - 0.5) * (x - 0.5) + (y - 0.5) * (y - 0.5)) < 0.25)
                    {
                        //Interlocked.Increment(ref N_0); 
                        N_0++; // coordinate in a circle! mark it by incrementing N_0
                    }
                }
            }
        }
    }

不平行的模拟:

class TCalcPi//unparallel calculating method
    {
        int N;
        int N_0;
        double aPi;
        public StringBuilder Msg; // diagnostic message
        double x, y;
        Stopwatch stopWatch = new Stopwatch();

        public void Init(int aN)
        {
            stopWatch.Start();
            N = aN; // save total calculate-iterations amount
            aPi = -1; // flag, if no any calculate-iteration has been completed
            Msg = new StringBuilder("No any calculate-iteration has been completed");
        }
        public void Run()
        {
            if (N < 1)
            {
                Msg = new StringBuilder("Invalid N - value");
                return;
            }

            int i;
            Random rnd = new Random(); // to create randomizer
            for (i = 1; i <= N; i++)
            {
                x = rnd.NextDouble(); // to generate coordinates
                y = rnd.NextDouble(); // 
                if (((x -  0.5) * (x -  0.5) + (y -  0.5) * (y -  0.5)) <  0.25)
                {
                    N_0++; // coordinate in a circle! mark it by incrementing N_0
                }
            }
            aPi = 4.0 * ((double)N_0 / (double)N); // to calculate approximate Pi - value
            stopWatch.Stop();
            TimeSpan ts = stopWatch.Elapsed;
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds / 10);
            Console.WriteLine("RunTime " + elapsedTime);
        }
        public double Done()
        {
            if (aPi > 0)
            {
                Msg = new StringBuilder("Calculates has been completed successful");
                return aPi; // return gotten value
            }
            else
            {
                return 0; // no result
            }
        }
    }

2 个答案:

答案 0 :(得分:4)

您已经以PointGenerator的方式编写了lock,它几​​乎不会因并行执行而受益。

  • N_0意味着它将具有基本的单线程性能和额外的线程开销
  • 全局状态Interlocked表示您必须同步访问权限。当然,因为它只是一个int,你可以使用PointGenerator类来有效地递增它。

我想让每个Random具有不同的Random对象和不同的计数器。然后就不会有任何可能导致问题的共享可变状态。但要小心,PointGenerator的默认构造函数使用系统的滴答计数。创建多个对象可能会导致具有相同种子的随机生成器。

完成所有{{1}}后,您将结果合并。

这与Parallel.ForParallel.ForEach的某些TPL重载非常类似。

答案 1 :(得分:2)

当你的整个平行部分在锁定范围内时,实际上没有任何东西是平行的。在任何给定时刻,锁定范围内只能有一个线程。

您可以简单地使用不同的Random个实例而不是单个实例。