2016-07-20 24 views
6

我寫了一個C#類,用一些數據填充一個「雙打列表清單」(無所謂數據是什麼,現在它可能只是一些垃圾:)),用於測試目的:爲什麼在這個例子中使用比列表更快的元組?

下面是代碼:

class test 
    { 
    public test() 
    { 
     _myListOfList = new List<List<double>>(1000000); 
    } 
    public void Run() 
    { 
     for (int i = 0; i < _myListOfList.Capacity; i++) 
     { 
      _myListOfList.Add(
       new List<double>(3) { i, 10*i, 100*i} 
       ); //Populate the list with data 
     } 
    } 

    private List<List<double>> _myListOfList; 
} 

我比較了下面這個代碼的執行速度:(由元組替換的兩倍列表)

class test 
    { 
    public test() 
    { 
     _myListOfTuple = new List<Tuple<double, double, double>>(1000000); 
    } 
    public void Run() 
    { 
     for (int i = 0; i < _myListOfTuple.Capacity; i++) 
     { 
      _myListOfTuple.Add(
       new Tuple<double, double, double>(i, 10 * i, 100 * i) 
       ); //Populate the list with data 
     } 
    } 

    private List<Tuple<double, double, double>> _myListOfTuple; 
} 

原來,使用元組似乎要快得多。我跑這段代碼的不同目錄的大小(200000種元素 - 列表> 5百萬元)和這裏的結果我得到:

graph

我真的不能讓我的頭圍繞這一個。我如何得到如此顯着的差異?使用一個存儲相同類型對象的元組(雙倍於此)沒有多大意義。我寧願使用List /數組來做到這一點:我做錯了什麼?有沒有辦法讓案例#1比案例#2更快/更快地運行?

謝謝!

+2

爲什麼這令人驚訝呢?處理存儲任意數量的對象比存儲恰好3個對象需要更多的工作。 – Servy

+0

開銷。首先,與列表相比,你會期望多少空間會佔據雙打的位置?對於每種類型,您希望他們在內部具有哪些字段? –

+1

縮放甚至小的差異,足夠讓他們看起來很大.. – TaW

回答

7

new Tuple<double, double, double>(i, 10 * i, 100 * i)new List<double>(3) { i, 10*i, 100*i}之間的差異。

第一個是超級簡單 - 僅有3分配:

public Tuple(T1 item1, T2 item2, T3 item3) { 
    m_Item1 = item1; 
    m_Item2 = item2; 
    m_Item3 = item3; 
} 

第二個實際上是由編譯器變換爲3 Add方法調用:

var temp = new List<double>(3); 
temp.Add(i); 
temp.Add(10 * i); 
temp.Add(100 * i); 

Add不僅僅是一個更分配:

public void Add(T item) { 
    if (_size == _items.Length) EnsureCapacity(_size + 1); 
    _items[_size++] = item; 
    _version++; 
} 

更多代碼t運行,執行速度較慢。很簡單..

+0

感謝你的答案一堆..它使很多的意義!正如@Tigran所建議的那樣,我用一個數組替換了List(new int [3] {i,10 * i,100 * i}),並且在速度方面我得到了非常相似的結果:) – harveyAJ

2

正如@提到馬爾辛的回答這是真的,即使通過初始化列表IL初始化List<T>仍具有內部Add()功能,即使您最初指定,施工期間,列表的Capacity。就像你在你的例子中做的那樣。

有沒有辦法讓案例#1比案例#2運行得更快/更快?

的可能的解決方案是直接分配給成員:

list[0] = 
list[1] = 
list[2] = 

在這種情況下IL是這樣的:

IL_0000: ldc.i4.3  
IL_0001: newobj  System.Collections.Generic.List<System.Double>..ctor 
IL_0006: stloc.0  // list 
IL_0007: ldloc.0  // list 
IL_0008: ldc.i4.0  
IL_0009: ldc.r8  00 00 00 00 00 00 F0 3F 
IL_0012: callvirt System.Collections.Generic.List<System.Double>.set_Item 
IL_0017: ldloc.0  // list 
IL_0018: ldc.i4.1  
IL_0019: ldc.r8  00 00 00 00 00 00 24 40 
IL_0022: callvirt System.Collections.Generic.List<System.Double>.set_Item 
IL_0027: ldloc.0  // list 
IL_0028: ldc.i4.2  
IL_0029: ldc.r8  00 00 00 00 00 00 59 40 
IL_0032: callvirt System.Collections.Generic.List<System.Double>.set_Item 
IL_0037: ret 

set_Item更快,因爲它是一個簡單的任務。

或者,使用簡單的Array。性能應該更好。儘管如此,像A和B速度這樣的東西,只有在具體測量後才能得出真正的答案。

相關問題