2016-07-20 21 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速度这样的东西,只有在具体测量后才能得出真正的答案。

相关问题