Henrik Clausen
Henrik Clausen

Reputation: 729

C#, lambda : How are redundant calls handled?

Im curious about how the compiler handles the following expression:

    var collapsed = elements.GroupBy(elm => elm.OrderIdentifier).Select(group => new ModelsBase.Laser.Element()
    {
        CuttingDurationInSeconds = group.Sum(itm => itm.CuttingDurationInSeconds),
        FladderDurationInSeconds = group.Sum(itm => itm.FladderDurationInSeconds),
        DeliveryDate = group.Min(itm => itm.DeliveryDate),
        EfterFladderOpstilTid = group.First().EfterFladderOpstilTid,
        EfterRadanOpstilTid = group.First().EfterRadanOpstilTid,
    });

As you can see, I'm using group sum twice, so does anyone know if the "group" list will be iterated twice to get both sums, or will it be optimized so there is actually only 1 complete iteration of the list.

Upvotes: 1

Views: 137

Answers (3)

Tohm
Tohm

Reputation: 305

You can optimise your request by adding two field in the grouping key

var collapsed = elements.GroupBy(elm => new{
   OrderIdentifier=elm.OrderIdentifier,
   EfterFladderOpstilTid=elm.EfterFladderOpstilTid,
   EfterRadanOpstilTid=elm.EfterRadanOpstilTid
})
   .Select(group => new ModelsBase.Laser.Element()
        {
            CuttingDurationInSeconds = group.Sum(itm => itm.CuttingDurationInSeconds),
            FladderDurationInSeconds = group.Sum(itm => itm.FladderDurationInSeconds),
            DeliveryDate = group.Min(itm => itm.DeliveryDate),
            EfterFladderOpstilTid = group.Key.EfterFladderOpstilTid,
            EfterRadanOpstilTid   = group.Key.EfterRadanOpstilTid,
        });

Or by using LET statement

var collapsed = from groupedElement in 
                        (from element in elements
                         group  element by element.OrderIdentifier into g
                        select g)
                        let First = groupedElement.First()
                        select  new ModelsBase.Laser.Element()
                        {
                            CuttingDurationInSeconds = groupedElement.Sum(itm => itm.CuttingDurationInSeconds),
                            FladderDurationInSeconds = groupedElement.Sum(itm => itm.FladderDurationInSeconds),
                            DeliveryDate = groupedElement.Min(itm => itm.DeliveryDate),
                            EfterFladderOpstilTid = First.EfterFladderOpstilTid,
                            EfterRadanOpstilTid = First.EfterRadanOpstilTid
                        };

Upvotes: 1

Holger
Holger

Reputation: 2654

LINQ ist most often not the best way to reach high performance, what you get is productivity in programming, you get a result without much lines of code.

The possibilities to optimize is limited. In case of Querys to SQL, there is one rule of thumb: One Query is better than two queries.

1) there is only one round trip to the SQL_Server

2) SQL Server is made to optimize those queries, and optimization is getting better if the server knows, what you want to do in the next step. Optimization is done per query, not over multiple queries.

In case of Linq to Objects, there is absolutely no gain in building huge queries. As your example shows, it will probably cause multiple iterations. You keep your code simpler and easier to read - but you give up control and therefore performance.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1500805

The compiler certainly won't optimize any of that.

If this is using LINQ to Objects, and therefore delegates, the delegate will iterate over each group 5 times, for the 5 properties.

If this is using LINQ to SQL, Entity Framework or something similar, and therefore expression trees, then it's basically up to the query provider to optimize this appropriately.

Upvotes: 1

Related Questions