c# - `Where` 中新对象的 Linq 查询性能

标签 c# performance linq lambda compiler-optimization

这之间是否存在性能差异:

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

还有这个:

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

?

bar 是某种动态集时可能不是(然后查询被转换为 SQL)。
但是如果它是一个简单的枚举呢?

最佳答案

是的,有一个真正的区别:后者使用对实例化一次的字符串的引用,但前者调用每次加载字符串的 lambda,就像每次评估条件目标部分而不是分配的任何循环一样它之前的值(value),什么时候可以完成以及编译器不知道如何优化自己的时候。

调用代码:

// 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'

实例方法中的编译器 lambda 未优化:

// 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

编码器的内联预分配:

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

从 lambda 转换而来的实例方法现在是:

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

因此,第一个需要较少的字符串对象实例创建,因此需要较少的内存和时间,这显然只有在大型集合上才能察觉到。

底层 IEnumerable 集合是什么以及元素来自何处并不重要。

但是对于一个IQueryable来说,IL代码就像是写了一个查询理解语法,针对这个值进行了优化检查,不管多复杂。

lambda 语法不仅转换为理解查询,就好像我们使用了类似函数式的流畅调用链一样:

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

关于c# - `Where` 中新对象的 Linq 查询性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67669456/

相关文章:

c# - 在比较对象时,如何让 NUnit 更具体地说明哪些字段不匹配?

php - 定期将数据内容复制到 MySQL 表中的最优雅的方法是什么?

c# - 从串口读取和发送数据

c# - "The project ' 迁移到 .NET Core 3 后 Web ' must provide a value for Configuration"错误

ios - 当我的应用程序启动时,我可以加载所有选项卡的数据吗?

performance - Berkeley DB Java 版 - 调整大量数据

c# - 为什么 LINQ 不允许我对我的数据点之一进行排序?

c# - 使用 LINQ 在字符串中查找单词

c# - 如何展平列表中的数据,然后使用 LINQ 获取每列中的内容计数?

c# - 如何使用本地系统用户通过 Windows 服务访问共享资源?