2012-05-29 133 views
3

我這樣做:爲什麼C#Parallel.Invoke很慢?

private static void Main(string[] args) 
    { 
     var dict1 = new Dictionary<int, string>(); 
     var dict2 = new Dictionary<int, string>(); 
     DateTime t1 = DateTime.Now; 
     for (int i = 1; i < 1000000; i++) 
     { 
      Parallel.Invoke(
       () => dict1.Add(i, "Test" + i), 
       () => dict2.Add(i, "Test" + i)); 
     } 
     TimeSpan t2 = DateTime.Now.Subtract(t1); 

     Console.WriteLine(t2.TotalMilliseconds); 

     Console.ReadLine(); 
    } 

所以做一個for循環百萬時間和添加項目到兩個不同的字典。 問題是,它需要11秒,這是超過5倍的正常順序方法(沒有任務/線程),只需要2秒。 不知道爲什麼。

+3

你確定這是做你想做的嗎?這隻做2次並行操作1000000次。你確定你不想讓你的Invoke調用中的循環? –

+3

不相關,但秒錶是這樣的時機好一點。 –

+0

好吧,我在這裏做的是測試同時填充多個字典的性能。我的真實情況是從數據庫加載所有實體並根據某些Key字段填充多個字典,然後緩存這些字典。 – Luka

回答

10

與其他人所說或暗示的一樣,由於並行化的開銷,並行代碼並不總是更快。

話雖這麼說,你的代碼是將項目添加到字典2並聯1M次而你應該增加100萬件到2個字典並行。差別很小,但最終的結果是代碼比我們的順序案例快了10%(在我的機器上)。

Parallel.Invoke(() => FillDictionary(dict1, 1000000),() => FillDictionary(dict2, 1000000)); 

... 

private static void FillDictionary(Dictionary<int, string> toFill, int itemCount) 
{ 
    for(int i = 0 ; i < itemCount; i++) 
     toFill.Add(i, "test" + i); 
} 
6

使用並行調用和在多個內核/ CPU之間分配工作的好處有一定的開銷。在這種情況下,開銷比有用工作分配的實際收益要大,所以這就是爲什麼你看到顯着差異的原因。嘗試使用更重的操作,你會看到不同之處。

+1

此外,在所有任務完成之前,'Parallel.Invoke'不會返回。在這種情況下,OP在同一個循環中進行兩個呼叫而不是一個呼叫,因此工作量增加了一倍,因此完成時間也增加了一倍。 – scottm

+0

我沒有繁重的操作,只是將詞條添加到詞典 – Luka

1

Parallel.Invoke表示「執行所有這些任務並等待完成」。在這種情況下,你只能同時執行兩項任務。因此並行調用的開銷大於併發的潛在收益。

5

重寫喜歡的東西:

Parallel.Invoke(
    () => 
    { 
     for (int i = 0; i < 1000000; i++) 
     { 
      dict1.Add(i, "Test" + i); 
     } 
    }, 
    () => 
    { 
     for (int i = 0; i < 1000000; i++) 
     { 
      dict2.Add(i, "Test" + i); 
     } 
    } 
); 

這應該是快了很多,因爲兩個線程被初始化恰好一次,然後運行至完成。在你的版本中,你正在調用每個lambda表達式1000000次,每次都在等待兩個完成之前完成。

Parallel.Invoke確實意味着用於更長時間的運行操作。否則,設置並行任務並等待它們全部完成的開銷只會殺死通過並行運行任務所獲得的任何性能。

0

如果你想要做的是將100000項添加到兩個不同的字典,你應該打破任務之間的工作量,而不是方法。這種方法和你的代碼一樣,但是在我的機器上比你的實現快得多:

var dict1 = new ConcurrentDictionary<int, string>(); 
    var dict2 = new ConcurrentDictionary<int, string>(); 

    Parallel.Invoke(() => 
    { 
     for(int i = 0; i < 500000; i++) 
     { 
      dict1[i] = "Test" +i; 
      dict2[i] ="Test" +i; 
     } 
    }, 
    () => 
    { 
     for(int i = 500000; i < 1000000; i++) 
     { 
      dict1[i] ="Test" +i; 
      dict2[i] = "Test" +i; 
     } 
    }); 
+0

此方法也不是線程安全的。您不能同時修改兩個線程中的一個字典。 – svick

+0

@svick在這種情況下,我並不想保證線程安全。你可以使用ConcurrentDictionary來保證線程安全(我修改了我的例子)。 – scottm

+1

@scottm:ConcurrentDictionary沒有添加方法... –