Lukasz Madon
Lukasz Madon

Reputation: 15004

Linq performance and delayed execution

I have run some tests for .Net CF. Basically, I wanted to compare for, foreach, extenstion method ForEach and LINQ query. Here is the whole code (you can skip it, to get to the point which bothers me)

namespace ForEachForLINQPerTest
{
 class IntBox
 {
    public int fieldX;
    public int PropertyX { get; set; }
 }

 public partial class MainPage : PhoneApplicationPage
 {
    /// <summary>
    /// size of tested List
    /// </summary>
    public const int TEST_SIZE = 1000000;

    //
    private List<int> m_intList = new List<int>(TEST_SIZE);

    //
    private List<IntBox> m_intBoxList = new List<IntBox>(TEST_SIZE);

    //
    private Stopwatch m_stopwatch = null;
    // Constructor
    public MainPage()
    {
        InitializeComponent();

        for (int i = 0; i < TEST_SIZE; ++i)
        {
            m_intBoxList.Add( new IntBox());
            m_intList.Add(0);
        }
    }

    private void startButton_Click(object sender, RoutedEventArgs e)
    {
        var forTest = ForTest(); // Jitter preheat
        forTest = ForTest();
        forResultTextBlock.Text = forTest;

        var foreachTest = ForEachTest();
        foreachTest = ForEachTest();
        foreachResultTextBlock.Text = foreachTest;

        var exTest = Extenstion();
        exTest = Extenstion();
        ExtensionResultTextBlock.Text = exTest;

        var linqTest = LINQTest();
        linqTest = LINQTest();
        LINQResultTextBlock.Text = linqTest;
    }

    private string LINQTest()
    {
        m_stopwatch = new Stopwatch();
        m_stopwatch.Start();
        long temp = 0;
        var result = from x in m_intList
                     select temp += x;            
        m_stopwatch.Stop();
        var intListTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();
        result.ToList();


        m_stopwatch.Start();
        var result2 = from x in m_intBoxList
                     select temp += x.fieldX;    
        m_stopwatch.Stop();
        var intBoxListFieldTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();
        result2.ToList();

        m_stopwatch.Start();
        var result3 = from x in m_intBoxList
                      select temp += x.PropertyX;    
        m_stopwatch.Stop();
        var intBoxListPropertyTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();
        result3.ToList();
        return String.Format("LINQ test List<int> = {0} \n List<IntBox> field = {1} \n List<IntBos> property = {2}", intListTime, intBoxListFieldTime, intBoxListPropertyTime); 
    }

    private string Extenstion()
    {
        m_stopwatch = new Stopwatch();
        m_stopwatch.Start();
        long temp = 0;
        m_intList.ForEach(i => temp += i);
        m_stopwatch.Stop();
        var intListTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();


        m_stopwatch.Start();
        m_intBoxList.ForEach(i => temp += i.fieldX);
        m_stopwatch.Stop();
        var intBoxListFieldTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();

        m_stopwatch.Start();
        m_intBoxList.ForEach(i => temp += i.PropertyX);
        m_stopwatch.Stop();
        var intBoxListPropertyTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();
        return String.Format("Extenstion test List<int> = {0} \n List<IntBox> field = {1} \n List<IntBos> property = {2}", intListTime, intBoxListFieldTime, intBoxListPropertyTime); 
    }

    private string ForEachTest()
    {
        m_stopwatch = new Stopwatch();
        long temp = 0;
        m_stopwatch.Start();
        foreach(int item in m_intList)
        {
           temp += item;
        }
        m_stopwatch.Stop();
        var intListTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();


        m_stopwatch.Start();
        foreach (IntBox item in m_intBoxList)
        {
            temp += item.fieldX;
        }
        m_stopwatch.Stop();
        var intBoxListFieldTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();

        m_stopwatch.Start();
        foreach (IntBox item in m_intBoxList)
        {
            temp += item.PropertyX;
        }
        m_stopwatch.Stop();
        var intBoxListPropertyTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();

        return String.Format("ForEach test List<int> = {0} \n List<IntBox> field = {1} \n List<IntBos> property = {2}", intListTime, intBoxListFieldTime, intBoxListPropertyTime); 
    }

    private string ForTest()
    {
        m_stopwatch = new Stopwatch();
        m_stopwatch.Start();
        long temp = 0;
        for (int i = 0; i < TEST_SIZE; ++i)
        {
            temp += m_intList[i];
        }
        m_stopwatch.Stop();
        var intListTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();


        m_stopwatch.Start();
        for (int i = 0; i < m_intList.Count; ++i)
        {
            temp += m_intBoxList[i].fieldX;
        }
        m_stopwatch.Stop();
        var intBoxListFieldTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();

        m_stopwatch.Start();
        for (int i = 0; i < m_intList.Count; ++i)
        {
           temp +=  m_intBoxList[i].PropertyX;
        }
        m_stopwatch.Stop();
        var intBoxListPropertyTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();

        return String.Format("For loop test List<int> = {0} \n List<IntBox> field = {1} \n List<IntBos> property = {2}", intListTime, intBoxListFieldTime, intBoxListPropertyTime); 
    }
 }
}

And here I am confused

        m_stopwatch = new Stopwatch();
        m_stopwatch.Start();
        long temp = 0;
        var result = from x in m_intList
                     select temp += x;            
        m_stopwatch.Stop();
        var intListTime = m_stopwatch.ElapsedMilliseconds;
        m_stopwatch.Reset();
        result.ToList();

The ouput is:

For loop test List = 93
List field = 119 // ref -> field
List property = 136 // ref -> property -> field properties are just functions for CF

ForEach test List = 88
List field = 140
List property = 152

Extenstions test List = 176
// another function is called. List field = 220
List property = 239

LINQ test List = 0 Why?
List field = 163
List property = 165

Why intListTime == 0? What am I doing wrong? Also the last two values for field and property are almost the same (run it a few times). Does it mean that PropertyX in LINQ query is evaluated in-line?

Upvotes: 1

Views: 762

Answers (2)

VinayC
VinayC

Reputation: 49215

The first time is zero because expression tree is built at compile time and it gets evaluated on ToList call that you have not included in timing.

For field and property access timing, I wouldn't worry too much - in reality, in release build, simple property accessor will be get inlined giving same performance as field access. For linq case, you might be seeing the same performance because linq internally might be converting property/field access into a method call and it would result in same timings (as I believe that method call overhead will be probably large compared to the field/prop access.

Upvotes: 3

Robert Levy
Robert Levy

Reputation: 29083

this is called "deferred execution". the linq statement isn't evaluated until it needs to be. move the ToList to be before you stop the clock and the time will go up

Upvotes: 2

Related Questions