Linq 性能和延迟执行

Posted

技术标签:

【中文标题】Linq 性能和延迟执行【英文标题】:Linq performance and delayed execution 【发布时间】:2011-06-12 22:00:45 【问题描述】:

我已经为 .Net CF 运行了一些测试。基本上,我想比较 for、foreach、扩展方法 ForEach 和 LINQ 查询。这是整个代码(你可以跳过它,以达到困扰我的地步)

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

我很困惑

        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();

输出是:

For 循环测试 List = 93 List field = 119 // ref -> field List property = 136 // ref -> property -> 字段属性只是 CF 的函数

ForEach 测试列表 = 88 列表字段 = 140 列表属性 = 152

Extensions test List = 176 // 另一个函数被调用。 列表字段 = 220 列表属性 = 239

LINQ 测试列表 = 0 为什么? 列表字段 = 163 列表属性 = 165

为什么intListTime == 0?我究竟做错了什么?此外,字段和属性的最后两个值几乎相同(运行几次)。这是否意味着 LINQ 查询中的 PropertyX 是内联评估的?

【问题讨论】:

您在问题标题中说了这一切:“延迟执行”。执行被延迟,直到(在这种情况下)ToList 被调用。 查看 Albahari 网站上的 Linq 测验:albahari.com/nutshell/linqquiz.aspx。这有一些非常有用的解释和明显“奇怪”的 LINQ 行为的例子。 @Kirk ye 我知道,但我不知道为什么第一个 LINQ 是 0 而其他的是 160ms。在所有情况下,ToList 都在时间之外,所以都应该是 0? 好吧 LINQ 有延迟执行;其他方法没有延迟执行。 【参考方案1】:

第一次为零,因为表达式树是在编译时构建的,它会在您未包含在计时中的 ToList 调用上进行评估。

对于字段和属性访问时间,我不会太担心 - 实际上,在发布版本中,简单的属性访问器将被内联,提供与字段访问相同的性能。对于 linq 情况,您可能会看到相同的性能,因为 linq 内部可能会将属性/字段访问转换为方法调用,并且会导致相同的时间(因为我相信与字段/属性相比,方法调用开销可能会很大访问。

【讨论】:

好的。但是我没有在其他两个 LINQ 的计时中包含 ToList,但它显示了一些时间。这是什么? @lukas - 抱歉没有解释清楚,但在第一种情况下,表达式树是为延迟执行而构建的,而在后来的情况下,不会因为字段/属性访问而延迟执行。后者是一个典型的案例 - 对象的 linq 通常在当时和那里得到评估。第一种情况非常简单,因此会导致构建表达式树 - 请参阅此 SO 线程,在已接受的答案中对此进行了很好的解释:***.com/questions/2649874/…【参考方案2】:

这称为“延迟执行”。直到需要时才评估 linq 语句。将 ToList 移动到停止时钟之前,时间会增加

【讨论】:

以上是关于Linq 性能和延迟执行的主要内容,如果未能解决你的问题,请参考以下文章

linq延迟状态的执行

linq之延迟加载和即时加载+标准查询运算符

大量数据的linq查询延迟结果

Lambda的延迟执行

Linq的初步了解

LINQ中的First()会导致急切或延迟加载吗?