繁体   English   中英

使用TPL分割周期

[英]To split for-cycle using TPL

为了并行化和加速计算,我使用TPL将一个长循环分为两个短循环,在下面的类中,这些部分称为PointGenerator和PointGenerator2:

class CalcPiTPL
    {
        int n;
        int totalCounter;
        int counter1;
        int counter2;
        double aPi;
        public StringBuilder Msg; // diagonstic message
        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;
            }

            Task[] tasks = new Task[2];
            tasks[0] = Task.Factory.StartNew((obj) => { PointGenerator((int)obj); }, n);
            tasks[1] = Task.Factory.StartNew((obj) => { PointGenerator2((int)obj); }, n);

            Task.WaitAll(tasks[0], tasks[1]);
            totalCounter = counter1 + counter2;
            aPi = 4.0 * ((double)totalCounter / (double)n); // to calculate approximate Pi - value
            Console.WriteLine(aPi);
            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)//FIRST PART OF ONE BIG FOR-CYCLE
        {
            double s = 0.125;
            double sP = s / (n / 2);
            double x = Math.Sqrt(sP);
            for (double cX = 0; cX <= 0.25; cX += x)
            {
                for (double cY = 0; cY <= 0.5; cY += x)
                {
                    if (((cX - 0.5) * (cX - 0.5) + (cY - 0.5) * (cY - 0.5)) < 0.25)
                    {
                        counter1++; // coordinate in a circle! mark it by incrementing N_0
                    }
                }
            }
        }

        public void PointGenerator2(int n)//SECOND PART OF ONE BIG FOR-CYCLE
        {
            double s = 0.125;
            double sP = s / (n / 2);
            double x = Math.Sqrt(sP);
            for (double cX = 0.25; cX <= 0.5; cX += x)
            {
                for (double cY = 0; cY <= 0.5; cY += x)
                {
                    if (((cX - 0.5) * (cX - 0.5) + (cY - 0.5) * (cY - 0.5)) < 0.25)
                    {
                        counter2++; // coordinate in a circle! mark it by incrementing N_0
                    }
                }
            }
        }
    }

这是不使用Tasks(TPL)的同一类,它有一个较长的周期:

class TCalcPi//unparallel calculating method
    {
        int N;
        int n_0;
        double aPi;
        public StringBuilder Msg; // diagnostic message


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

            double s = 0.25;
            double sP = s / N;
            double x = Math.Sqrt(sP);
            for (double cX = 0; cX <= 0.5; cX += x)//ONE LONG FOR-CYCLE
            {
                for(double cY = 0; cY <= 0.5; cY += x)
                {
                    if (((cX - 0.5) * (cX - 0.5) + (cY - 0.5) * (cY - 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
            }
        }
    }

但是非并行化类比并行化(使用TPL)类工作得更快。 如何解决?

counter1counter2最有可能位于同一高速缓存行,因为它们在内存中相邻。 这会导致错误共享 可能您经常增加这些计数器。 对于计数器之间的每个时间交替,这会ping通两个内核的L1之间的高速缓存行。

分开他们。 作为概念证明,如下所示:

int counter1;
long padding0, p1, p2, p3, p4, p5, p6, p7; //64 bytes padding
int counter2;

希望JIT不会对字段进行重新排序。 也许您需要使用StructLayout

或者,使计数器成为局部变量。 堆栈变量仅在极端偶然的情况下才具有错误共享。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM