2012-10-05 76 views
4

我想優化泛型列表算術運算。我有3個可空的double列表,如下定義。泛型列表性能優化

List<double?> list1 = new List<double?>(); 
List<double?> list2 = new List<double?>(); 
List<double?> listResult = new List<double?>(); 

int recordCount = list1.Count > list2.Count ? list2.Count : list1.Count; 

for (int index = 0; index < recordCount; index++) 
{ 
     double? result = list1[index] + list2[index]; 
     listResult.Add(result); 
} 

有什麼辦法讓這個操作運行得更快,如果我有巨大的列表?

感謝您的輸入。

+0

在將它們添加到一起之前,如何填充列表?如果數據來自數據庫,那麼從dB獲得結果總和會更快。 –

+0

您知道如果2個列表具有不同的大小,此代碼將產生ArrayOutOfBoundsException? –

+1

@juergend不,他首先發現哪個列表更短 - 第5行。 – Jay

回答

9

有什麼辦法使這個操作運行速度更快,如果我有巨大的名單?

你可以將你的列表創建的結果,直到你開始計數後:

List<double?> list1 = new List<double?>(); 
List<double?> list2 = new List<double?>(); 

int recordCount = list1.Count > list2.Count ? list2.Count : list1.Count; 
List<double?> listResult = new List<double?>(recordCount); 

這將讓你指定所需結果的實際容量,避免列表本身內重新分配。對於「巨大的列表」,這可能是最慢的部分之一,因爲內存分配和列表中的副本變大將是最慢的操作。

而且,如果計算很簡單,你可能會使用多個內核:

List<double?> list1 = new List<double?>(); 
List<double?> list2 = new List<double?>(); 

int recordCount = list1.Count > list2.Count ? list2.Count : list1.Count; 

var results = new double?[recordCount]; // Use an array here 

Parallel.For(0, recordCount, index => 
    { 
     double? result = list1[index] + list2[index]; 
     results[index] = result; 
      }); 

鑑於「工作」就是這裏這麼簡單,你可能確實需要自定義分區,以獲得最大的並行性(見How to: Speed Up Small Loop Bodies瞭解詳細信息):

var results = new double?[recordCount]; // Use an array here 
var rangePartitioner = Partitioner.Create(0, recordCount); 

Parallel.ForEach(rangePartitioner, range => 
    { 
     for (int index = range.Item1; index < range.Item2; index++) 
     { 
      results[index] = list1[index] + list2[index]; 
     } 
      }); 

如果這不是一個瓶頸,但是,你可以使用LINQ要做到這一點作爲一個班輪:

var results = list1.Zip(list2, (one, two) => one + two).ToList(); 

然而,這將是(非常輕微),比處理自己的循環效率較低,如果性能確實是一個瓶頸。

+0

我很確定列表realloc會使用某種類型的增長/指數/ fibonnaci /其他擴展增長系統,所以也許不會如你所想。但我同意只分配一次是一個很好的優化,而且容易。 – mattypiper

+0

@mattypiper它的確如此 - 它從4個元素開始,每次翻倍。但是,如果列表非常大,那麼可能會很昂貴,因爲每個realloc都需要現有列表的副本。 –

+0

'Zip'提示+1, –

0

如果你知道列表的大小時間提前,簡單數組應該運行得更快。創建這樣的:

double?[] Array1 = new double?[10]; 
0

你可以做的第一件事就是指定你的結果的

List<double?> listResult = new List<double?>(recordCount); 

這將預先分配的結果節省時間,每個List.Add的空間容量()調用。

如果你真的擔心性能,你可以打破名單成塊,並引發一場多線程處理完成後做的部分結果集,然後合併全套重新走到一起。

0
var result = from i in 
      Enumerable.Range(0, Math.Min(list1.Count, list2.Count)) 
      select list1.ElementAtOrDefault(i) + list2.ElementAtOrDefault(i); 
foreach (var item in result) 
{ 
Debug.Write(item.Value); 
} 
0

如果必須使用原始數組而不是列表的能力,你當然可以使這個速度更快 - 到底有多少,取決於你想怎麼去低級別的。糾正你的來源中的一些錯誤,我寫了三個不同的版本。通過爲結果創建一個新列表(我冒昧地使用具有容量的構造函數,防止了一堆中間分配),可以實現代碼的功能。

我還寫道,總結兩個數組到第三,同認爲,剝去名單將提高效率的版本。

最後,我寫了一個版本,使用不安全的代碼將第一個數組添加到第二個使用指針。

比較的結果是以下:

Timings: 500000 iterations of 10000-item lists 
    Lists:   00:00:13.9184166 
    Arrays (safe): 00:00:08.4868231 
    Arrays (unsafe): 00:00:03.0901603 

Press any key to continue... 

我使用的代碼可以在this Github gist找到。

不安全的代碼可能會有點太優化,但看到這三個樣本之間的差異是相當驚人的。爲了清楚起見,我建議堅持使用安全的代碼並使用數組。