2016-10-10 152 views
1

我有两个在内存中的戏剧和消费者之一有15密耳的对象和另一个约3密尔。大列表上的plinq需要很长的时间

以下几个疑问我正在射击的..

consumersn=consumers.AsParallel() 
        .Where(w => plays.Any(x => x.consumerid == w.consumerid)) 
        .ToList(); 


List<string> consumerids = plays.AsParallel() 
           .Where(w => w.playyear == group_period.year 
             && w.playmonth == group_period.month 
             && w.sixteentile == group_period.group) 
           .Select(c => c.consumerid) 
           .ToList(); 


int groupcount = plays.AsParallel() 
         .Where(w => w.playyear == period.playyear 
           && w.playmonth == period.playmonth 
           && w.sixteentile == group 
           && consumerids.Any(x => x == w.consumerid)) 
         .Count(); 

我使用16芯机,32 GB的RAM,这inspite ..第一个查询花了大约20小时运行..

我做错了什么..

所有的帮助是真诚的赞赏。

感谢

+1

分析器是你的朋友在这里。但是看起来你在这里第一次查询时正在做15M * 3M的操作。 –

回答

2

第一个LINQ查询是非常低效的,并行只能帮你这么多。

说明:当你写consumers.Where(w => plays.Any(x => x.consumerid == w.consumerid)),这意味着,在每consumer对象,你将有可能遍历整个plays列表中找到受影响的消费者。所以这是最多300万消费者的1500万次播放= 45万亿次操作。即使跨越16个内核,也就是每个内核约2.8万亿次运算。

所以,这里的第一步是将所有剧本由他们consumerIds,并缓存结果以适当的数据结构:

var playsByConsumerIds = plays.ToLookup(x => x.consumerid, StringComparer.Ordinal); 

然后,你的第一个要求变为:

consumersn = consumers.Where(w => playsByConsumerIds.Contains(w.consumerid)).ToList(); 

即使没有任何并行化,该查询应该快得多。

我无法修复以下查询,因为我没有完全看到的确切操作,但我建议使用GroupByToLookup来一次性创建所有组。

+0

谢谢..我会阅读ToLookup。就其他两个查询而言..他们发生在一个foreach循环内部,并且没有使用那里的groupby子句。在第二个查询中获得消息标识的唯一原因是能够在第三个查询中使用它。所以将它改变是正确的。在第二个查询中选择(c => c.consumerid)为.ToLookup(x => x.consumerid,StringComparer.Ordinal);和consumerids.Any(x => x == w.consumerid)to consumerids.Contains(w.consumerid) – Arnab

+0

我的建议是,您可以使用GroupBy来避免第二个和第三个查询的循环。 –

+0

对不起,无法理解你使用'GroupBy'来避免循环的意思..你能否提供一个例子..第二个和第三个查询中的where子句分别使用来自不同来源的值group_period和(period和group).. – Arnab

1

第一个查询需要20小时才能运行,因为plays.Any(x => x.consumerid == w.consumerid)需要遍历整个15,000,000个播放列表,每次consumerid都不在那里。

您可以通过构建一个哈希集合所有消费者的ID在plays,这样加快这:

var consumerIdsInPlays = new HashSet<string>(plays.Select(p => p.consumerid)); 

现在你的第一个查询可以改写为O(1)查找:

consumersn=consumers 
    .AsParallel() 
    .Where(w => consumerIdsInPlays.Contains(w.consumerid)) 
    .ToList(); 
+0

可以使用Hashset进行第二个查询并会更快?也应该在第三个查询中将consumerids.Any(x => x == w.consumerid)更改为consumerids.Contains(w.consumerid)。会更快吗? Tx – Arnab

+1

@Arnab第二个查询不搜索列表,所以它不会更快。如果将'consumerids'设置为哈希集合,并使用'Contains'来代替'Any',则可以更快地查询第三个查询。你也可以用'Count(cond)'替换'Where(cond).Count()'。 – dasblinkenlight