如何使用任务并行性并行运行多个循环 [英] How to run multiple loops parallely using task parallelism

查看:139
本文介绍了如何使用任务并行性并行运行多个循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嗨朋友们,



我的要求是,我需要进行蒙特卡罗分析才能找到成功的可能性。每年我需要使用预模拟值运行1000次迭代。如果说分析范围是20年,它花费了很多时间来完成任务。我认为当前年度终值的复杂性应该是明年的起始值。我的问题是如何应用任务并行或线程来提高性能。我将简要介绍程序逻辑



第1年

迭代1

起始价值:10
//计算逻辑

结束价值:12

迭代2

起始价值是12

//计算逻辑

结束价值是13



就像明智的1000次迭代一样。



第2年

迭代1

起始值:12(上一年迭代1结束值)

//计算逻辑

结束值:14

迭代2

起始值:13(去年迭代2结束价值)

//计算逻辑

结束价值:16

类似明智的1000次迭代



需要存储所有1000年迭代值,并需要按升序对值进行排序,并从每年数组中获取第5个第50和第95个值。



示例代码如下。

请建议一些可以提高性能的好方法。



我尝试过的方法:



示例代码

Hi Friends,

My requirement is, I need to perform Monte Carlo analysis to find probability of success. Each year I need to run for 1000 iteration with pre simulated values. If say the analysis horizon is 20 years its taking to much time to finish the task. The complexity I feel is current year ending value should be next year starting value. My question is how can I apply task parallelism or threading to increase the performance. I will briefly explain the program logic

Year 1
Iteration 1
Starting Value: 10
//calculation logic
Ending Value: 12
Iteration 2
Starting Value is 12
//calculation logic
Ending Value is 13

like wise 1000 iterations.

Year 2
Iteration 1
Starting value : 12 ( previous year iteration 1 ending value)
//calculation logic
ending value :14
Iteration 2
Starting value : 13 ( previous year iteration 2 ending value)
//calculation logic
ending value :16
like wise 1000 iterations

Need to store all years 1000 iterations values and need to sort the values in an ascending order and take 5th 50th and 95th value from each year array.

Sample code is given below.
Please suggest some good approach which can increase the performance.

What I have tried:

Sample code

    static void Main(string[] args)
    {
        List<LinearGrowth> lstlg = new List<LinearGrowth>();
        Reference.dividendP = 0.11;
        Reference.interestP = 0.07;
        Reference.feeP = 0.05;
        List<CurrentAllocation> lstca = new List<CurrentAllocation>();
        lstca.Add(new CurrentAllocation { ID = 1, aloc = 0.3 });
        lstca.Add(new CurrentAllocation { ID = 2, aloc = 0.2 });
        lstca.Add(new CurrentAllocation { ID = 3, aloc = 0.4 });
        lstca.Add(new CurrentAllocation { ID = 4, aloc = 0.1 });
        List<Probability> lstprob = new List<Probability>();

        DateTime dt = DateTime.Now;
        Console.WriteLine(dt);
        double pval = 0;
        int loopcount = -1;
        for (int i = DateTime.Now.Year; i <= DateTime.Now.Year + 20; i++)
        {
            Console.WriteLine("Year: {0}", i);
            Probability objprob = new Probability();
            lstlg = new List<LinearGrowth>();
            for (int j = 0; j <= 100; j++)
            {
                if (i == DateTime.Now.Year)
                {
                    if (i == DateTime.Now.Year && j == 0)
                        pval = 1000;
                    else
                        pval = lstlg[j- 1].endingvalue;
                }
                else
                {
                    pval = lstprob[loopcount].yearvalues[j].endingvalue;
                }


                LinearGrowth lg = new LinearGrowth();
                lg = CalculationMethods.Montecarlo(lstca, pval);
                lstlg.Add(lg);
                Console.WriteLine("Starting Value:{0} Ending Value {1}", Math.Round(lg.startingvalue,2), Math.Round(lg.endingvalue,2));
            }
            objprob.year = i;
            objprob.yearvalues = lstlg.OrderBy(o => o.endingvalue).ToList<LinearGrowth>();
            lstprob.Add(objprob);
            Console.WriteLine("Year: {0}", i);
            Console.WriteLine("5th:{0} 50th: {1}  95{2}", objprob.yearvalues[4].endingvalue, objprob.yearvalues[49].endingvalue, objprob.yearvalues[94].endingvalue);
            loopcount = loopcount + 1;
        }
        Console.WriteLine(DateTime.Now);
        TimeSpan t = DateTime.Now - dt;
        Console.WriteLine(t.Seconds);
        Console.ReadLine();
    }
}







public class LinearGrowth
   {
       public double startingvalue { get; set; }
       public double fees { get; set; }
       public double tax { get; set; }
       public double endingvalue { get; set; }
   }
   public class Reference
   {
       public static double interestP { get; set; }
       public static double dividendP { get; set; }
       public static double feeP { get; set; }
   }
   public class Probability
   {
       public int year { get; set; }
       public List<LinearGrowth> yearvalues { get; set; }
   }

   public class CurrentAllocation
   {
       public int ID { get; set; }
       public double aloc { get; set; }
   }
   public class CalculationMethods
   {
       public static LinearGrowth Montecarlo(List<CurrentAllocation> lstca, double pv)
       {
           LinearGrowth lg = new LinearGrowth();
           Random rnd = new Random();
           double allocation, interest, dividend, fees, annualreturn;
           double sum_int, sum_div, sum_fee,sum_alloc, tax;
           sum_int = sum_div = sum_fee = tax =sum_alloc= 0;
           for (int i = 0; i < lstca.Count; i++)
           {
               allocation = pv * lstca[i].aloc;
               annualreturn = allocation * rnd.NextDouble();
               interest = allocation * Reference.interestP;
               dividend = allocation * Reference.dividendP;
               fees = allocation * Reference.feeP;
               sum_int += interest;
               sum_div += dividend;
               sum_fee += fees;
               sum_alloc += annualreturn;
           }
           tax = (sum_int + sum_div) * 0.15;
           lg.startingvalue = pv;
           lg.fees = sum_fee;
           lg.tax = tax;
           lg.endingvalue = pv + sum_int + sum_div - tax - sum_fee;
           return lg;
       }
   }

推荐答案

对于初学者来说,你有一个非常微妙的错误会导致如果你碰巧在12月31日晚上开始这个错误的结果!你在很多地方打电话给 DateTime.Now.Year 。这可能会在运行期间发生变化,导致错误的结果!您已在 Main()的开头附近的 dt 中捕获 DateTime.Now ,在其他地方使用 ,例如:

For starters, you have a very subtle bug that would cause incorrect results if you happened to start this late in the evening on December 31! You are calling DateTime.Now.Year in lots of places. That could change during running, leading to incorrect results! You have already captured DateTime.Now into dt near the beginning of Main(), use that everywhere else, like:
for (int i = dt.Year; i <= dt.Year + 20; i++)



由于这对您的性能问题没有帮助,我将在此时留下这个建议。 ;-)



表现:

这是一个有趣的问题所以我花了(超过我应该有时间。



看来,对于第1年,迭代的起始值是前一次迭代的值,而剩下的年份迭代的起始值是前一年相同迭代的值#。

因此,首先运行一年级循环,将起始值从迭代链接到迭代。当每次迭代结束时,然后启动一个计算该迭代剩余年份的任务。

对于1000次迭代,这将 启用 大约1000x并行! 注意:除非你的内核超过1000个,否则 得到这么多并行性!但它应该能够使用与系统必须提供的并行功能一样多的并行功能。

另一个考虑因素是,任何非特定年份的集合都可能需要更改为线程安全的集合(除非在完成其他所有操作之前不使用它们)。



所以这就是我想出的:


Since this won't help with your performance issue, I'll leave this suggestion at this point. ;-)

Performance:
This was an interesting problem so I spent (more than I should have) time on it.

It appears that for Year 1, an iteration's starting value is the previous iteration's value, while for the remaining years an iteration's starting value is the previous year's value for the same iteration #.
So, first run a Year 1 loop, chaining the starting values from iteration to iteration. As each iteration ends, then start a Task that computes the remaining years of that iteration.
For 1000 iterations, this will enable about a 1000x parallelism! NOTE: You will not get this much parallelism unless you have more than 1000 cores! But it should be able to use just about as much parallel capability as the system has to offer.
Another consideration is that any collections that are not year-specific will probably need to be changed to thread-safe collections (unless they aren't used until everything else is done).

So here's what I came up with:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApplication11
{
  class Program
  {
    const int Iterations = 100;
    const int Years = 20;
    const double InitialValue = 1000;

    static void Main(string[] args)
    {
      Reference.dividendP = 0.11;
      Reference.interestP = 0.07;
      Reference.feeP = 0.05;
      List<CurrentAllocation> lstca = new List<CurrentAllocation>() {
                                            new CurrentAllocation { ID = 1, aloc = 0.3 },
                                            new CurrentAllocation { ID = 2, aloc = 0.2 },
                                            new CurrentAllocation { ID = 3, aloc = 0.4 },
                                            new CurrentAllocation { ID = 4, aloc = 0.1 }
                                          };

      DateTime dt = DateTime.Now;
      Console.WriteLine(dt);
      double pval = InitialValue;
      Task<List<LinearGrowth>>[] workers = new Task<List<LinearGrowth>>[Iterations];
      List<LinearGrowth>[] growthByYear = new List<LinearGrowth>[Years];
      List<LinearGrowth> firstYearGrowth = new List<LinearGrowth>(Iterations);
      Random rand = new Random(CalculationMethods.MakeSeed());  // one, common, random number generator for this Year 1 sequence
      for (int iter = 0; iter < Iterations; iter++)
      {
        LinearGrowth lg = CalculationMethods.Montecarlo(lstca, pval, rand);
        firstYearGrowth.Add(lg);
        workers[iter] = CalculationMethods.SpinUpGrowthOverYearsTask(dt.Year + 1, Years - 1, pval, lstca);
        pval = lg.endingvalue;
      }

      Task.WaitAll(workers);
      growthByYear[0] = firstYearGrowth;
      for (int yearOffset = 1; yearOffset < Years; yearOffset++)
      {
        growthByYear[yearOffset] = new List<LinearGrowth>(Iterations);
      }

      // this basically "transposes" from iteration collections to year collections.
      foreach (var iterTask in workers)
      {
        var yearsResults = iterTask.Result;
        int yearOffset = 1;
        foreach (var result in yearsResults)
        {
          growthByYear[yearOffset++].Add(result);
        }
      }

      List<Probability> lstprob = new List<Probability>();
      int year = dt.Year;
      foreach (var yearGrowth in growthByYear)
      {
        Probability objprob = new Probability();
        objprob.year = year;
        objprob.yearvalues = yearGrowth.OrderBy(o => o.endingvalue).ToList();
        lstprob.Add(objprob);
        // TODO: The yearvalues index "magic numbers" should be replaced by named constants!
        // I'll leave that to you to choose good names!
        Console.WriteLine("Year: {0}\n5th: {1} 50th: {2}  95th: {3}",
                          year++,
                          objprob.yearvalues[4].endingvalue,
                          objprob.yearvalues[49].endingvalue,
                          objprob.yearvalues[94].endingvalue);
      }

      DateTime end = DateTime.Now;
      Console.WriteLine("Ending time: {0}", end);
      TimeSpan t = end - dt;
      Console.WriteLine("Elapsed time: {0}", t);
      Console.ReadLine();
    }
  }

  public class CalculationMethods
  {
    public static int MakeSeed()
    {
      long ticks = DateTime.Now.Ticks;
      return (int)((ticks >> 32) ^ ticks);
    }
    public static Task<List<LinearGrowth>> SpinUpGrowthOverYearsTask(int startYear, int numberOfYears, double initial, IEnumerable<CurrentAllocation> currentAllocations)
    {
      Random ran = new Random(MakeSeed());  // one, common, random number generator for this Task, because Random.NextDouble() isn't thread-safe!
      return Task.Run<List<LinearGrowth>>(() => GrowthOverYears(startYear, numberOfYears, initial, currentAllocations, ran));
    }
    public static List<LinearGrowth> GrowthOverYears(int startYear, int numberOfYears, double initial, IEnumerable<CurrentAllocation> currentAllocations, Random ran)
    {
      List<LinearGrowth> result = new List<LinearGrowth>(numberOfYears);
      for (int yearOffset = 0; yearOffset < numberOfYears; yearOffset++)
      {
        LinearGrowth lg = CalculationMethods.Montecarlo(currentAllocations, initial, ran);
        result.Add(lg);
        initial = lg.endingvalue;
      }
      return result;
    }

    // I brought the random number generator OUT so there's less chance of the same sequence on sequential calls.
    public static LinearGrowth Montecarlo(IEnumerable<CurrentAllocation> currentAllocations, double pv, Random ran)
    {
      LinearGrowth lg = new LinearGrowth();
      double allocation, interest, dividend, fees, annualreturn;
      double sum_int, sum_div, sum_fee, sum_alloc, tax;
      sum_int = sum_div = sum_fee = tax = sum_alloc = 0;
      foreach (var ca in currentAllocations)
      {
        allocation = pv * ca.aloc;
        annualreturn = allocation * ran.NextDouble();
        interest = allocation * Reference.interestP;
        dividend = allocation * Reference.dividendP;
        fees = allocation * Reference.feeP;
        sum_int += interest;
        sum_div += dividend;
        sum_fee += fees;
        sum_alloc += annualreturn;
      }
      tax = (sum_int + sum_div) * 0.15;
      lg.startingvalue = pv;
      lg.fees = sum_fee;
      lg.tax = tax;
      lg.endingvalue = pv + sum_int + sum_div - tax - sum_fee;
      return lg;
    }
  }

  public class LinearGrowth
  {
    public double startingvalue { get; set; }
    public double fees { get; set; }
    public double tax { get; set; }
    public double endingvalue { get; set; }
  }
  public class Reference
  {
    public static double interestP { get; set; }
    public static double dividendP { get; set; }
    public static double feeP { get; set; }
  }
  public class Probability
  {
    public int year { get; set; }
    public List<LinearGrowth> yearvalues { get; set; }
  }
  public class CurrentAllocation
  {
    public int ID { get; set; }
    public double aloc { get; set; }
  }
}



注意:当你将它分成多个任务时,无法保证每个任务中的 Console.WriteLine(...)都会出现在同一个顺序为顺序。 警告经纪人!(上面,我从任务中删除了所有 Console.WriteLine(...)



享受!


Note: when you divide this into multiple Tasks, there's no guarantee that the Console.WriteLine(...) from each Task, will all come out in the same sequence as sequential. Caveat emptor! (Above, I removed all of the Console.WriteLine(...) from the tasks.)

Enjoy!


您好,



用于优化代码和时间您的代码可以用于某些优化技术,如并行编程代替循环



< a href =http://www.c-sharpcorner.com/UploadFile/f9f215/parallel-programming-part-1-introducing-task-programming-l/>并行编程的参考链接


谢谢,

Sisir Patro
Hi,

For optimizing the code and the time taken by your code you can go for some optimizing technics like "Parallel Programming" in place of loops.

Reference link to Parallel programming

Thanks,
Sisir Patro


这篇关于如何使用任务并行性并行运行多个循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆