在虚拟机上使用AsParallel()和/或Parallel.ForEach

时间:2014-04-13 09:41:33

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

我们的网络应用程序托管在具有8个vCPU的虚拟机上。我们有一个密集的数据操作,运行在每晚的时间表(控制台应用程序/ Windows任务调度程序),我想以某种方式并行化。该操作在许多数据集上迭代多次以计算不同的统计数据。目前,当它运行时,任务管理器显示其CPU使用率从未超过13%。

以下是其中一个被调用方法的代码(Web应用程序是一个大问卷):

Dictionary<string, List<decimal>> decimalStats = new Dictionary<string, List<decimal>>();

using (var db = new PDBContext())
{
    IEnumerable<FinancialYear> financialYears = db.FinancialYears;
    IEnumerable<Section> sections;
    IEnumerable<Question> questions;

    IQueryable<int> orgIds = db.Organisations.Where(l => l.Sector.IndustryID == 1).Select(m => m.OrganisationID);
    IQueryable<int> subSectionIds;

    foreach (var financialYear in financialYears)
    {
        sections = db.Sections.Where(l => orgIds.Contains(l.OrganisationID) && l.FinancialYearID == financialYear.FinancialYearID && l.IsVerified.Value);

        foreach (var section in sections)
        {
            subSectionIds = db.SubSections.Where(l => l.SectionID == section.SectionID).Select(m => m.SubSectionID);                
            questions = db.Questions.Where(l => subSectionIds.Contains(l.SubSectionID.Value));

            foreach (var question in questions)
            {
                var answer = db.Answers.Where(l => l.QuestionID == question.QuestionID && l.OrganisationID == section.OrganisationID && l.FinancialYearID == financialYear.FinancialYearID).FirstOrDefault();

                if (answer != null)
                {
                    string key = question.QuestionID + "#" + financialYear.FinancialYearID;

                    decimal val;
                    if (decimal.TryParse(answer.Text, out val))
                    {
                        if (decimalStats.ContainsKey(key))
                        {
                            ((List<decimal>)decimalStats[key]).Add(val);
                        }
                        else
                        {
                            List<decimal> vals = new List<decimal>();
                            vals.Add(val);
                            decimalStats.Add(key, vals);
                        }
                    }
                }
            }
        }
    }

    foreach (KeyValuePair<string, List<decimal>> entry in decimalStats)
    {
        List<decimal> vals = ((List<decimal>)entry.Value).OrderBy(l => l).ToList();

        if (vals.Count > 0)
        {
            // lots of stuff to calculate various statistics about the data
        }
    }
}

我已经将代码简化了很多。我希望它能隔离我可以使用某些并行执行的区域。

我尝试了不同的使用组合:

IEnumerable<FinancialYear> financialYears = db.FinancialYears.AsParallel();

Parallel.ForEach(financialYears, financialYear => { });

{{{ 1}}

...但我没有做任何事情将CPU使用率推高到13%以上,执行该方法所花费的时间几乎保持不变。我在这里错过了什么伎俩?并行编程对我来说是新的,所以我试图尽可能简单地使用PLINQ / TPL。

1 个答案:

答案 0 :(得分:1)

问题很可能出在数据库查询中,而不是CPU中。

我建议您尽量减少查询次数并最大限度地增加来自这些查询的数据量,而不是尝试并行化CPU操作。

例如这一行:

var answer = db.Answers.Where(l => l.QuestionID == question.QuestionID && l.OrganisationID == section.OrganisationID && l.FinancialYearID == financialYear.FinancialYearID).FirstOrDefault();

可能性能问题,因为它每年都会打击数据库,部分和问题,这很多。您应该更喜欢使用单个查询将每个内容预加载到内存中,并使用内存数据。

另外,我忘了提及:在尝试任何类型的性能优化之前,您应该分析您的代码。这样,您就知道您的问题是I / O绑定还是算法,这将决定您应该优化代码的方式。