【问题标题】:c# Linq to Objects - FirstOrDefault performancec# Linq to Objects - FirstOrDefault 性能
【发布时间】:2013-09-03 05:35:02
【问题描述】:

我们正在尝试优化我们的一些方法。我们使用 Redgate 的性能分析器来查找一些性能漏洞。

我们的工具以多种方法使用 Linq 对象。但我们注意到 FirstOrDefault 处理包含 +/- 1000 个对象的集合需要很长时间。

分析器还会提醒查询非常慢。我已经添加了带有分析器结果的图像。

无法将集合添加到数据库然后查询数据库。 有什么建议吗?

谢谢!

private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant)
{
    SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null;

    if (saldoPrivatiefKlantVerdeelsleutel != null)
    {
        try
        {
            parentSaldoPrivatiefKlantVerdeelsleutel = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection
                .FirstOrDefault(s => (boeking == null || (s.SaldoVerdeelsleutel != null &&
                (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID)))
                && s.PrivatiefKlant.ID == privatiefKlant);
        }
        catch (Exception ex)
        { }
    }

    return parentSaldoPrivatiefKlantVerdeelsleutel;
}

图片: Profile report

【问题讨论】:

  • 我的第一个想法是将boeking == null 检查和SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID移出您的循环。没有理由为您收藏中的每个元素重新评估它。
  • 从表达式中读取时执行查询。在这种情况下,FirstOrDefault 将执行整个表达式树,所以不是 FirstOrDefault 慢。
  • 必填项:空catch{}非常有害,删除它。
  • 现在我要问你一个问题...对象是通过 NHibernate 或实体框架加载的,对吗?你确定它们没有部分延迟加载吗?喜欢这个s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID 吗?因为它可能是经典的SELECT N+1 antipattern

标签: c# performance linq


【解决方案1】:

你可以试着把它写成一个简单的代码。 LINQ 正在使用委托,这就是性能受到一点影响的原因。

                try
                {
                    parentSaldoPrivatiefKlantVerdeelsleutel = null;
                    foreach (var s in saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection)
                    {
                        if ((boeking == null || (s.SaldoVerdeelsleutel != null && (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant)
                        {
                            parentSaldoPrivatiefKlantVerdeelsleutel = s;
                            break;
                        }
                    }
                }
                catch (Exception ex)
                { }

【讨论】:

    【解决方案2】:

    FirstOrDefault 对源集合执行标准线性搜索并返回与谓词匹配的第一个元素。这是 O(n),因此在更大的集合上花费更多时间也就不足为奇了。

    你可以尝试跟随,但收益不会很大,因为它仍然是O(n)

    private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant)
    {
        SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null;
    
        if (saldoPrivatiefKlantVerdeelsleutel != null)
        {
            try
            {
                var query = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode
                                                             .SaldoPrivatiefKlantVerdeelsleutelCollection
                                                             .Where(s => s.PrivatiefKlant.ID == privatiefKlant);
    
                if(boeking != null)
                {
                    var gebouwVerdeelSleutelId = boeking.SaldoGebouwRekeningVerdeling
                                                        .SaldoGebouwRekening
                                                        .SaldoVerdeelsleutel
                                                        .GebouwVerdeelSleutel
                                                        .ID;
    
                    query = query.Where(s => s.SaldoVerdeelsleutel != null &&
                        s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == gebouwVerdeelSleutelId);
                }
                parentSaldoPrivatiefKlantVerdeelsleutel = query.FirstOrDefault();
            }
            catch (Exception ex)
            { }
        }
    
        return parentSaldoPrivatiefKlantVerdeelsleutel;
    }
    

    它会变得更好,因为boeking != null 检查只会进行一次,而不是在每个源集合元素上进行。而且因为nested Where calls are combined 他们不会造成性能损失。

    【讨论】:

      【解决方案3】:

      我会说这个

      boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID
      

      可能是罪魁祸首。尝试将其缓存在外部,例如:

      var id = boeking != null ? boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID : 0;
      

      并在查询中使用id

      (我在做一个假设:长链的一个属性做了一些“不太聪明”的事情,实际上很慢)

      【讨论】:

        【解决方案4】:

        你应该可以通过重写来加速它

        saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection
                    .Where(s => (boeking == null || (s.SaldoVerdeelsleutel != null &&
                        (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant)
                    .FirstOrDefault()
        

        请参阅 Why is LINQ .Where(predicate).First() faster than .First(predicate)? 了解为什么这样更快。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-30
          • 2018-03-03
          • 1970-01-01
          • 2010-11-05
          • 2011-10-22
          • 1970-01-01
          • 1970-01-01
          • 2021-03-17
          相关资源
          最近更新 更多