2012-12-20 120 views
5

可能重複:
c# Leaner way of initializing int array如何初始化整數數組C#

基本上我想知道是否有除了以下

private static int[] GetDefaultSeriesArray(int size, int value) 
    { 
     int[] result = new int[size]; 
     for (int i = 0; i < size; i++) 
     { 
      result[i] = value; 
     } 
     return result; 
    } 
所示的一個比較有效碼

其中大小可以從10變化到150000.對於小型陣列不是問題,但應該有更好的w ay做到以上。我正在使用VS2010(.NET 4.0)

+0

你正在給你的數組的所有字段設置「值」? – cheesemacfly

+0

是的,他將每個元素設置爲一個非默認的int值 - 這可能是除了微型優化之外最快的方法(某人毫無疑問會顯示一些微型優化技術) –

+1

無論你如何操作,初始化非數組C#中的默認值是O(N)操作。 – Maciej

回答

4

可以提高速度的一種方法是利用Array.Copy。它能夠在較低的級別上工作,在這個級別上,它可以批量分配更大的內存部分。

通過批量分配,您最終可以將數組從一個部分複製到其自身。

最重要的是,批次本身可以非常有效地並行。

這是我最初的代碼。在我的機器上(它只有兩個核心)和一個1000萬個物品的樣品陣列,我的速度提高了15%左右。您需要充分利用批量大小(儘量保持頁面大小的倍數以保持高效),以將其調整爲您擁有的項目大小。對於較小的陣列,它最終會與代碼幾乎相同,因爲它不會填滿第一批,但在這些情況下也不會(明顯)更糟。

private const int batchSize = 1048576; 
private static int[] GetDefaultSeriesArray2(int size, int value) 
{ 

    int[] result = new int[size]; 

    //fill the first batch normally 
    int end = Math.Min(batchSize, size); 
    for (int i = 0; i < end; i++) 
    { 
     result[i] = value; 
    } 

    int numBatches = size/batchSize; 

    Parallel.For(1, numBatches, batch => 
    { 
     Array.Copy(result, 0, result, batch * batchSize, batchSize); 
    }); 

    //handle partial leftover batch 
    for (int i = numBatches * batchSize; i < size; i++) 
    { 
     result[i] = value; 
    } 

    return result; 
} 
+0

+1。好的建議。你有沒有機會比較單獨(無並行)拷貝的改進? –

+0

@AlexeiLevenkov一些簡單的測試讓它接近相同。這需要進行調整以獲得更多好處(例如,您只需擁有合適的批處理大小)。 – Servy

8

C#/ CLR沒有內置的方式來初始化非默認值的數組。

如果您測量每個項目的操作,那麼您的代碼的效率與它可能得到的效率一樣高。

如果您並行初始化巨大數組的塊,您可以獲得更快的初始化。由於多線程操作的非平凡成本,這種方法需要仔細調整。

通過分析您的需求並可能刪除整個初始化,可以獲得更好的結果。即如果數組通常包含常量值,則可以實現某種COW(寫入時複製)方法,其中對象最初沒有後備數組,並且simpy返回常量值,寫入要創建的元素(可能是部分)的後備數組修改段。

更慢但更緊湊的代碼(可能更容易閱讀)將使用Enumerable.Repeat。請注意0​​將導致大量內存分配給大型陣列(這也可能會導致在LOH上分配) - High memory consumption with Enumerable.Range?

var result = Enumerable.Repeat(value, size).ToArray(); 
+5

這將會相當*效率較低*,而不是*效率更高*。 – Servy

+0

@Servy:這可能就是爲什麼Alexei在給出這個答案之前說「你的代碼和它能得到的一樣高效」。 –

+0

@HonzaBrestan嗯,我不同意這種說法。除此之外,這個問題要求更好的選擇。如果你沒有更好的選擇,那麼就不要回答,而不是提供你認爲更糟糕的東西。 – Servy

0
Enumerable.Repeat(value, size).ToArray(); 
-2

如前所述,您可以利用並行處理這樣的一個人:

int[] result = new int[size]; 
Parallel.ForEach(result, x => x = value); 
return result; 

對不起我沒有時間做這個性能測試(不具備這個安裝VS機器),但如果你可以做到這一點,並分享結果,那就太好了。

編輯:根據評論,而我仍然認爲,在表現他們是等價的術語,你可以嘗試並行for循環:

Parallel.For(0, size, i => result[i] = value); 
+0

這不是一個並行化的有效方法。這幾乎肯定會變得更糟,因爲你會失去內存本地化,而工作單元太小。如果你使用了parallel.for,至少它會有更好的鏡頭。 – Servy

+0

@ EDIT:你總是可以很容易地測試表現,然後你就不用猜測了。 –

+1

我剛剛用這兩種方法進行了一些簡單的測試,並行版本花費了大約5倍的時間,這與我的預期相當。 – Servy

0

讀了Enumerable.Repeat是慢20倍ops標準for循環和我發現的唯一可以提高其速度的東西是

private static int[] GetDefaultSeriesArray(int size, int value) 
{ 
    int[] result = new int[size]; 
    for (int i = 0; i < size; ++i) 
    { 
     result[i] = value; 
    } 
    return result; 
} 

注意i ++更改爲++ i。 i ++複製i,增加i,並返回原始值。 ++我只是返回遞增的值

1

提高性能的另一種方法是使用非常基本的技術:循環展開。

我已經寫了一些代碼來初始化一個數組有2000萬個項目,這是重複執行100次,並計算出平均值。如果不展開循環,則需要大約44個MS。隨着循環展開10,該過程在23個MS中完成。

private void Looper() 
     { 
      int repeats = 100; 
      float avg = 0; 

      ArrayList times = new ArrayList(); 

      for (int i = 0; i < repeats; i++) 
       times.Add(Time()); 

      Console.WriteLine(GetAverage(times)); //44 

      times.Clear(); 

      for (int i = 0; i < repeats; i++)    
       times.Add(TimeUnrolled()); 

      Console.WriteLine(GetAverage(times)); //22 

     } 

private float GetAverage(ArrayList times) 
     { 
      long total = 0; 
      foreach (var item in times) 
      { 
       total += (long)item; 
      } 

      return total/times.Count; 
     } 

     private long Time() 
     { 
      Stopwatch sw = new Stopwatch(); 
      int size = 20000000; 
      int[] result = new int[size]; 
      sw.Start(); 


      for (int i = 0; i < size; i++) 
      { 
       result[i] = 5; 
      } 
      sw.Stop(); 
      Console.WriteLine(sw.ElapsedMilliseconds); 
      return sw.ElapsedMilliseconds; 
     } 

     private long TimeUnrolled() 
     { 
      Stopwatch sw = new Stopwatch(); 
      int size = 20000000; 
      int[] result = new int[size]; 
      sw.Start(); 


      for (int i = 0; i < size; i += 10) 
      { 
       result[i] = 5; 
       result[i + 1] = 5; 
       result[i + 2] = 5; 
       result[i + 3] = 5; 
       result[i + 4] = 5; 
       result[i + 5] = 5; 
       result[i + 6] = 5; 
       result[i + 7] = 5; 
       result[i + 8] = 5; 
       result[i + 9] = 5; 
      } 
      sw.Stop(); 
      Console.WriteLine(sw.ElapsedMilliseconds); 
      return sw.ElapsedMilliseconds; 
     } 
+0

不是最好的代碼,但它顯示了我的觀點。總的來說,這是一個50%的提高,如果你這樣做一次,但沒有多少增加。 – Mataniko