简体   繁体   中英

Linq query performance with new object in `Where`

Is there a performance difference between this:

var listOfFoo = bar.Where(x => x.Id == new Guid("sth")).toList();

and this:

var guid = new Guid("sth");
var listOfFoo = bar.Where(x => x.Id == guid).toList();

?

Probably not when bar is some kind of dynamic set (then query is converted to SQL).
But if it's a simple enumerable?

Yes, there is a real difference: the latter uses a reference to the string instantiated once, but the former calls the lambda which loads the string each time, like any loop where the condition goal part is evaluated every time instead of assigning its value before, when it can be done and when the compiler doesn't know how to optimize itself.

Calling code:

// List<Guid> source2 = source.Where((Guid x) => x == new Guid("sth")).ToList();
IL_000e: ldloc.1
IL_000f: ldsfld class [mscorlib]System.Func`2<valuetype [mscorlib]System.Guid, bool> ConsoleApp.Program/'<>c'::'<>9__29_0'

Compiler lambda factored in an instance method is not optimized:

// return x == new Guid("sth");
IL_0000: ldarg.1
IL_0001: ldstr "sth"
IL_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
IL_000b: call bool [mscorlib]System.Guid::op_Equality(valuetype [mscorlib]System.Guid, valuetype [mscorlib]System.Guid)
IL_0010: ret

Coder's inlined preallocation:

// Guid guid = new Guid("sth");
IL_0046: ldstr "sth"
IL_004b: newobj instance void [mscorlib]System.Guid::.ctor(string)
IL_0050: stfld valuetype [mscorlib]System.Guid ConsoleApp.Program/'<>c__DisplayClass29_0'::guid

// source2 = source.Where((Guid x) => x == guid).ToList();
IL_0055: ldloc.1
IL_0056: ldloc.0
IL_0057: ldftn instance bool ConsoleApp.Program/'<>c__DisplayClass29_0'::'<Test>b__1'(valuetype [mscorlib]System.Guid)

The instance method converted from the lambda is now:

// return x == guid;
IL_0000: ldarg.1
IL_0001: ldarg.0
IL_0002: ldfld valuetype [mscorlib]System.Guid ConsoleApp.Program/'<>c__DisplayClass29_0'::guid
IL_0007: call bool [mscorlib]System.Guid::op_Equality(valuetype [mscorlib]System.Guid, valuetype [mscorlib]System.Guid)

Therefore the first requires less string object instances creation, thus less memory and less time, which would obviously only be perceptible on large collections.

It does not matter what the underlying IEnumerable collection and where the elements come from.

But for an IQueryable , the IL code is like writing a query comprehension syntax, and it is optimized for this value to check, regardless of the more complex.

The lambda syntax is not just converted to a comprehension query as if we had used functional-like fluent call chain:

var query = listOfFoo.AsQueryable().Where(x => x == new Guid("sth")).ToList();

//  List<Guid> list = (from x in source.AsQueryable()
//      where x == new Guid("sth")
//      select x).ToList();
IL_0007: ldloc.0
IL_0008: call class [System.Core]System.Linq.IQueryable`1<!!0> [System.Core]System.Linq.Queryable::AsQueryable<valuetype [mscorlib]System.Guid>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
// (no C# code)
IL_000d: ldtoken [mscorlib]System.Guid
IL_0012: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0017: ldstr "x"
IL_001c: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string)
IL_0021: stloc.2
IL_0022: ldloc.2
IL_0023: ldtoken method instance void [mscorlib]System.Guid::.ctor(string)
IL_0028: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
IL_002d: castclass [mscorlib]System.Reflection.ConstructorInfo
IL_0032: ldc.i4.1
IL_0033: newarr [System.Core]System.Linq.Expressions.Expression
IL_0038: dup
IL_0039: ldc.i4.0
IL_003a: ldstr "sth"
IL_003f: ldtoken [mscorlib]System.String
IL_0044: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0049: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type)
IL_004e: stelem.ref
IL_004f: call class [System.Core]System.Linq.Expressions.NewExpression [System.Core]System.Linq.Expressions.Expression::New(class [mscorlib]System.Reflection.ConstructorInfo, class [mscorlib]System.Collections.Generic.IEnumerable`1<class [System.Core]System.Linq.Expressions.Expression>)
IL_0054: ldc.i4.0
IL_0055: ldtoken method bool [mscorlib]System.Guid::op_Equality(valuetype [mscorlib]System.Guid, valuetype [mscorlib]System.Guid)
IL_005a: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
IL_005f: castclass [mscorlib]System.Reflection.MethodInfo
IL_0064: call class [System.Core]System.Linq.Expressions.BinaryExpression [System.Core]System.Linq.Expressions.Expression::Equal(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.Expression, bool, class [mscorlib]System.Reflection.MethodInfo)
IL_0069: ldc.i4.1
IL_006a: newarr [System.Core]System.Linq.Expressions.ParameterExpression
IL_006f: dup
IL_0070: ldc.i4.0
IL_0071: ldloc.2
IL_0072: stelem.ref
IL_0073: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<valuetype [mscorlib]System.Guid, bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[])
IL_0078: call class [System.Core]System.Linq.IQueryable`1<!!0> [System.Core]System.Linq.Queryable::Where<valuetype [mscorlib]System.Guid>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0, bool>>)
IL_007d: call class [mscorlib]System.Collections.Generic.List`1<!!0> [System.Core]System.Linq.Enumerable::ToList<valuetype [mscorlib]System.Guid>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM