2012-06-06 44 views
3

我試圖實現PLINQ的例子,但面臨以下問題 我的順序查詢比並行查詢執行得更快。PLINQ性能不佳

這裏是代碼示例:

 Stopwatch sw = new Stopwatch(); 

     int[] vals = Enumerable.Range(0, Int16.MaxValue).ToArray(); 

     sw.Start(); 
     int[] x1 = vals.Where(x => x % 2 == 0).ToArray(); 
     sw.Stop(); 
     Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds); 


     sw.Restart(); 
     int[] x2 = vals.AsParallel().Where(x => x % 2 == 0).ToArray(); 
     sw.Stop(); 
     Console.WriteLine("Parallel Execution {0} milliseconds", sw.ElapsedMilliseconds); 

我的機器是奔騰(R)雙 - 核心 我也試過在四 - 核AMD皓龍(TM)。

相同的結果並行查詢比順序運行速度慢。 你能告訴我什麼是我的問題?

謝謝。

+1

http://stackoverflow.com/questions/7582591/how-to-plinq-an-existing-linq-query-with-加入(閱讀我的推薦讀物)。簡而言之:你的模操作太微不足道了。你需要更復雜的操作。 –

回答

2

這一次似乎更好地工作:

 Stopwatch sw = new Stopwatch(); 

     int[] vals = Enumerable.Range(0, 10000000).ToArray(); 

     sw.Start(); 
     var x1 = vals.Where(x => x % 2 == 0).ToList(); 
     sw.Stop(); 
     Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds); 


     sw.Restart(); 
     var x2 = vals.Where(x => x % 2 == 0).AsParallel().ToList(); 
     sw.Stop(); 
     Console.WriteLine("Parallel Execution {0} milliseconds", sw.ElapsedMilliseconds); 

不啓動另一個線程200倍的值。它需要更多的啓動/喚醒其他線程,而不是在單個線程上完成整個循環。 +更多線程表示線程同步機制。 LE:好吧,我嘗試了Int16.MaxValue,它在那裏效果更好。我不知道最大值約爲30k,因此評論可能不適用於您的案例。可能問題在於AsParralel錯位。

+0

我剛剛嘗試了一些書籍的例子,我知道將集合分解爲少量數據的線程會比較耗時並行執行,但我沒有意識到32K值是不夠的。 –

5

我想這與一些開銷有關。 您迭代的集合非常小(32k短褲),並且對這些項目執行的操作是微不足道的。

在這種情況下,集合的劃分,過濾和重新合併可能比在單個迭代中執行集合要昂貴得多。

如果您的比較成本較高(例如搜索字符串)並且您的收藏增長,您會看到結果發生變化。

+0

是的,PLINQ不是一個能夠加快每個查詢速度的銀彈。當處理每個項目時會有延遲,比如從數據庫加載它時會發光。由於你的查詢沒有延遲,並且你無論如何都會最大限度地利用CPU,所以PLINQ的開銷會降低你的速度。 –

3

你的「問題」是使用PLINQ時,它沒有任何意義

PLINQ不會總是更快。 PLINQ將永遠增加開銷。

就CPU指令而言;無論您需要做多少工作(稱之爲X),您最終都會執行超過X條指令。 PLINQ將開展大量額外的工作,將工作委託給他們,並將結果返回到您可以使用的表單中。

這樣做的好處是你可以有多個CPU/Core的工作。有時它會更快。當你所做的CPU工作量相對於開銷很小時,它會變慢。

當我運行代碼,我得到以下的輸出:

順序執行2毫秒

並行執行40毫秒

我還可以看到正在創建工作線程由PLINQ代碼。這8個線程代表了2毫秒計算量的大量開銷。通過兩次運行「並行執行」基準測試,您可以感覺到它有多少開銷。工作線程將會四處流動。這是我的輸出與運行它的第二時間:

順序執行2毫秒

並行#1執行40毫秒

並行#2執行3毫秒

第二時間是快多了;但仍然比不做任何事情慢。因爲即使已經創建了工作線程 - PLINQ仍然需要做工作來分割線程之間的操作並以可以訪問的格式重新獲得結果。

您需要做的工作越多,對開銷的影響就越小。在這個例子中,我用一個名爲IsValid的靜態函數替換了你的Where lambda,並且我計算了%2 500次而不是一次。

static bool IsValid(int input) 
{ 
    int result=0; 

    for(int i =0;i<500;i++)    
     result = input%2; 

    return result == 0; 
} 

現在 - 我的執行時間是:

順序執行36毫秒

並行#1執行47毫秒

並行#2執行9毫秒

你可以看到PLINQ仍然很慢第一次執行 - 但在第二次執行時速度明顯加快。如果通過將環路從500增加到5000(在我的機器上)來實現CPU工作,PLINQ將勝出,而不用擔心。

TL; DR - 你做得對;你只是沒有做足夠的工作來讓PLINQ成爲更快的選擇。

下面是我做了什麼,整個源代碼:

static void Main(string[] args) 
{ 
    Stopwatch sw = new Stopwatch(); 

    int[] vals = Enumerable.Range(0, Int16.MaxValue).ToArray(); 

    sw.Start(); 
    int[] x1 = vals.Where(IsValid).ToArray(); 
    sw.Stop(); 
    Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds); 

    sw.Restart(); 
    int[] x2 = vals.AsParallel().Where(IsValid).ToArray(); 
    sw.Stop(); 
    Console.WriteLine("Parallel #1 Execution {0} milliseconds", sw.ElapsedMilliseconds); 

    sw.Restart(); 
    int[] x3 = vals.AsParallel().Where(IsValid).ToArray(); 
    sw.Stop(); 
    Console.WriteLine("Parallel #2 Execution {0} milliseconds", sw.ElapsedMilliseconds); 

    Console.Read(); 
} 

static bool IsValid(int input) 
{ 
    int result=0; 

    for(int i =0;i<5000;i++)    
     result = input%2; 

    return result == 0; 
} 
+0

您確定第二個並行查詢執行速度更快嗎?因爲線程已經啓動了嗎?如果你試圖執行相同的順序查詢兩次,它也更快,我只是認爲這是查詢緩存 – MaRuf