2016-10-10 151 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