Performance issues and concepts in LINQ

I have some questions / problems using LINQ in my projects. First question: is there a performance difference between old ( select Item from..

) linq and newer version ( .Select(r => ..)

)?

Second question: how is LINQ expresions translated (and into what)? Will it be translated to the old syntax first and then to something else (intermediate language)?

+3


source to share


2 answers


There is no difference between the two ways in which we can write a linq query.

In particular, this

var adults = from customer in customers
             where customer.Age>18
             select customer;

      

is equivalent to this:



var adults = customers.Where(customer=>customer.Age>18);

      

Actually, the compiler translates the first request into the second request. The first way to write a linq query is something like syntactic sugar. Under the hood, if you compile your code and then use the disassembler to view the IL code, you will notice that your request has been translated into the second of the above forms.

Queries written in the first way, we say that we have used query syntax. While the queries are written in the second way, we say that we used fluent syntax.

+6


source


Is there a performance difference between old ( select Item from..

) linq and newer version ( .Select(r => ..)

)?

Neither of them is older than the other, since both entered the language at the same time. If anything .Select()

could be argued as being older, since a method call would almost always be an extension method call (and therefore only available as of .NET 3.5 and only callable this way as of C # 3.0), there have been method calls in general, since 1.0.

There is no performance difference as they are different ways of saying the same thing. (It's almost possible that you can find a case that resulted in redundancy for one but not the other, but in most cases these dismissals are caught by the compiler and removed.)

How is LINQ expresions translated (and into what)? Will it be translated to the old syntax first and then to something else (intermediate language)?

Note that, as indicated above, from item in someSource select item.ID

and someSouce.Select(item => item.ID)

are the same thing. The compiler has to do two things:

  • Determine the calling method.
  • Determine how to use lambda in this.

The two go hand in hand. The first part is the same as with any other method call:

  • Look for a method defined on a type someSource

    that is called Select()

    and takes one parameter of the appropriate type (I'll pick up the "appropriate type" in a minute).

  • If no method is found, find the method defined in the immediate base of the type someSource

    , and so on until you have more base classes to explore (after reaching object

    ).

  • If no method is found, find the extension method defined for the static class, available for use via using

    that has the first ( this

    ) type parameter someSource

    , and its second parameter of the appropriate type, which I said, I'll be back in a minute.

  • If no method is found, find a generic extension method that can accept types someSource

    and lambda as parameters.

  • If none of the methods are found, follow the above two steps for the base types someSource

    and the interfaces it implements, continuing on to further base types or interfaces that extend those interfaces.

  • If the method is not found, raise a compiler error. Likewise, if any of the above steps found two or more equally applicable methods in the same step , raise a compiler error.

So far, this is the same as "".IsNormalized()

calling a method IsNormalized()

defined on string

, "".GetHashCode()

calling a method GetHashCode()

defined on object

(although a later step means overriding defined on string

is what actually gets executed), and "".GetType()

calling a method GetType()

defined on object

.

Indeed, we can see this in the following:

public class WeirdSelect
{
  public int Select<T>(Func<WeirdSelect, T> ignored)
  {
    Console.WriteLine("Select‎ Was Called");
    return 2;
  }
}
void Main()
{
  int result = from whatever in new WeirdSelect() select whatever;
}

      



Here, because it WeirdSelect

has its own applicable method Select

, which is executed in place of one of the extension methods defined in Enumerable

and Queryable

.

Now I manually waved over the "parameter of the appropriate type" above, because one complication that lambdas introduces to it is that a lambda in C # code can be turned into a delegate (in this case a Func<TSource, TResult>

where TSource

is the type of the lambdas parameter and TResult

return type) or an expression (in this case a Expression<Func<TSource, TResult>>

) in the generated CIL code.

Thus, method invocation resolution looks for either a method that will accept Func<TSource, TResult>

(or a similar delegate) or a method that will accept Expression<Func<TSource, TResult>>

(or a similar expression). If it finds and in the same search step , there will be a compiler error, so the following won't work:

public class WeirdSelect
{
  public int Select<T>(Func<WeirdSelect, T> ignored)
  {
    Console.WriteLine("Select‎ Was Called");
    return 2;
  }
  public int Select<T>(Expression<Func<WeirdSelect, T>> ignored)
  {
    Console.WriteLine("Select‎ Was Called on expression");
    return 1;
  }
}
void Main()
{
  int result = from whatever in new WeirdSelect() select whatever;
}

      

Now, 99.999% of the time, we are using Select

with something that implements IQueryable<T>

or something that does IEnumerable<T>

. If it implements IQueryable<T>

, then the method invocation resolution will find public static IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)

, defined in Queryable

, and if it implements IEnumerable<T>

, it will find public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)

, defined in Enumerable

. It doesn't matter which IQueryable<T>

comes from IEnumerable<T>

, because its method will be found earlier in the above process, before being IEnumerable<T>

considered as a base interface.

Therefore, 99.999% of the time will be called by one of these two extension methods. In the case, the IQueryable<T>

lambda turns into some code that generates a suitable one Expression

, which is then passed to the method (the query engine can then transform it to any code, for example, create the appropriate SQL queries if its database-backed query engine or something else otherwise case). In the case of IEnumerable<T>

lamda, it becomes an anonymous delegate that is passed to a method that looks a bit like:

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
  //Simplifying a few things, this code is to show the idea only
  foreach(var item in source)
    yield return selector(item);
}

      

To get back to your question:

Will it be translated to the old syntax first and then to something else (intermediate language)?

You could think of the new syntax from item in source select…

as being "turned into" the older syntax source.Select(…)

(but not very old as it depends on extension methods more than 99% of the time) since it calls the method call a bit clearer, but they actually make up the same thing ... In the CIL release, the differences depend on whether the call was an instance method or (as is almost always the case) an extension method, much less whether a lambda is used to express an expression or a delegate.

+1


source







All Articles