Why is Tuple faster than List in this example?

I wrote a C # class that populates the "List of Doubling Lists" with some data (no matter what the data is, at the moment it might just be garbage :)) for testing purposes:

Here is the code:

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; } 

I compared the execution speed of this code with the following: (replacing List of double with Tuple)

  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; } 

It turns out that using Tuple seems much faster. I ran this piece of code for different sizes of the list (from 200,000 items → 5 million items in the list), and here are the results that I get:

graph

I can not outline the head. Why am I getting such a big difference? Using a tuple that stores an object of the same type (doubles here) does not make much sense. I would prefer to use List / array for this: what am I doing wrong? Is there a way to make case # 1 run faster / faster than case # 2?

Thanks!

+6
source share
2 answers

There is a difference between new Tuple<double, double, double>(i, 10 * i, 100 * i) and new List<double>(3) { i, 10*i, 100*i} .

The first is super simple - only 3 tasks:

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

The second of them is actually converted by the compiler into method calls < Add :

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

Add much more than just an assignment:

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

More code to run, slower execution. Pretty simple ..

+7
source

As mentioned in @Marcin, believe that even initializing a List<T> IL initialization list still has the Add() function inside, even if you specify initially during the construction of the Capacity in the list. As in your example.

Is there a way to make case # 1 run faster / faster than case # 2?

A possible solution could be a direct assignment to members:

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

In this case, the IL looks like this:

 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 faster than a simple assignment.

Or use a simple Array . Performance should be better. However, with things like speed A versus B, the real answer is only after specific measurements.

+2
source

All Articles