我知道IQueryable和IEnumerable之间的区别,我知道Linq To Objects通过IEnumerable接口支持集合。
让我感到困惑的是,当集合转换为IQueryable时,查询执行速度提高了两倍。
让 l 成为列表类型的填充对象,如果列表 l 转换为linq查询,则速度提高两倍通过 l.AsQueryable() IQueryable 。
我用VS2010SP1和.NET 4.0编写了一个简单的测试来证明这一点:
private void Test()
{
const int numTests = 1;
const int size = 1000 * 1000;
var l = new List<int>();
var resTimesEnumerable = new List<long>();
var resTimesQueryable = new List<long>();
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
for ( int x=0; x<size; x++ )
{
l.Add( x );
}
Console.WriteLine( "Testdata size: {0} numbers", size );
Console.WriteLine( "Testdata iterations: {0}", numTests );
for ( int n = 0; n < numTests; n++ )
{
sw.Restart();
var result = from i in l.AsEnumerable() where (i % 10) == 0 && (i % 3) != 0 select i;
result.ToList();
sw.Stop();
resTimesEnumerable.Add( sw.ElapsedMilliseconds );
}
Console.WriteLine( "TestEnumerable" );
Console.WriteLine( " Min: {0}", Enumerable.Min( resTimesEnumerable ) );
Console.WriteLine( " Max: {0}", Enumerable.Max( resTimesEnumerable ) );
Console.WriteLine( " Avg: {0}", Enumerable.Average( resTimesEnumerable ) );
for ( int n = 0; n < numTests; n++ )
{
sw.Restart();
var result = from i in l.AsQueryable() where (i % 10) == 0 && (i % 3) != 0 select i;
result.ToList();
sw.Stop();
resTimesQueryable.Add( sw.ElapsedMilliseconds );
}
Console.WriteLine( "TestQuerable" );
Console.WriteLine( " Min: {0}", Enumerable.Min( resTimesQueryable ) );
Console.WriteLine( " Max: {0}", Enumerable.Max( resTimesQueryable ) );
Console.WriteLine( " Avg: {0}", Enumerable.Average( resTimesQueryable ) );
}
运行此测试(将 numTests == 1和10)会产生以下输出:
Testdata size: 1000000 numbers
Testdata iterations: 1
TestEnumerable
Min: 44
Max: 44
Avg: 44
TestQuerable
Min: 37
Max: 37
Avg: 37
Testdata size: 1000000 numbers
Testdata iterations: 10
TestEnumerable
Min: 22
Max: 29
Avg: 23,9
TestQuerable
Min: 12
Max: 22
Avg: 13,9
重复测试但是切换顺序(即首先测量IQuerable然后IEnumerable)会得到不同的结果!
Testdata size: 1000000 numbers
Testdata iterations: 1
TestQuerable
Min: 75
Max: 75
Avg: 75
TestEnumerable
Min: 25
Max: 25
Avg: 25
Testdata size: 1000000 numbers
Testdata iterations: 10
TestQuerable
Min: 12
Max: 28
Avg: 14
TestEnumerable
Min: 22
Max: 26
Avg: 23,4
以下是我的问题:
我问这些问题是因为我想知道哪一个用于我的存储库接口。现在他们在内存中查询集合(Linq to Objects),但将来这个可能是一个SQL数据源。如果我现在使用 IQueryable 设计存储库类,我可以轻松地稍后切换到Linq to SQL。然而,如果存在性能损失,那么坚持 IEnumerable ,而没有涉及SQL似乎更明智。
答案 0 :(得分:6)
使用linqpad检查IL代码,这是我所看到的:
对于此代码:
var l = Enumerable.Range(0,100);
var result = from i in l.AsEnumerable() where (i % 10) == 0 && (i % 3) != 0 select i;
生成:
IL_0001: ldc.i4.0
IL_0002: ldc.i4.s 64
IL_0004: call System.Linq.Enumerable.Range
IL_0009: stloc.0
IL_000A: ldloc.0
IL_000B: call System.Linq.Enumerable.AsEnumerable
IL_0010: ldsfld UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0015: brtrue.s IL_002A
IL_0017: ldnull
IL_0018: ldftn b__0
IL_001E: newobj System.Func<System.Int32,System.Boolean>..ctor
IL_0023: stsfld UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_0028: br.s IL_002A
IL_002A: ldsfld UserQuery.CS$<>9__CachedAnonymousMethodDelegate1
IL_002F: call System.Linq.Enumerable.Where
IL_0034: stloc.1
b__0:
IL_0000: ldarg.0
IL_0001: ldc.i4.s 0A
IL_0003: rem
IL_0004: brtrue.s IL_0011
IL_0006: ldarg.0
IL_0007: ldc.i4.3
IL_0008: rem
IL_0009: ldc.i4.0
IL_000A: ceq
IL_000C: ldc.i4.0
IL_000D: ceq
IL_000F: br.s IL_0012
IL_0011: ldc.i4.0
IL_0012: stloc.0
IL_0013: br.s IL_0015
IL_0015: ldloc.0
IL_0016: ret
对于这段代码:
var l = Enumerable.Range(0,100);
var result = from i in l.AsQueryable() where (i % 10) == 0 && (i % 3) != 0 select i;
我们得到了这个:
IL_0001: ldc.i4.0
IL_0002: ldc.i4.s 64
IL_0004: call System.Linq.Enumerable.Range
IL_0009: stloc.0
IL_000A: ldloc.0
IL_000B: call System.Linq.Queryable.AsQueryable
IL_0010: ldtoken System.Int32
IL_0015: call System.Type.GetTypeFromHandle
IL_001A: ldstr "i"
IL_001F: call System.Linq.Expressions.Expression.Parameter
IL_0024: stloc.2
IL_0025: ldloc.2
IL_0026: ldc.i4.s 0A
IL_0028: box System.Int32
IL_002D: ldtoken System.Int32
IL_0032: call System.Type.GetTypeFromHandle
IL_0037: call System.Linq.Expressions.Expression.Constant
IL_003C: call System.Linq.Expressions.Expression.Modulo
IL_0041: ldc.i4.0
IL_0042: box System.Int32
IL_0047: ldtoken System.Int32
IL_004C: call System.Type.GetTypeFromHandle
IL_0051: call System.Linq.Expressions.Expression.Constant
IL_0056: call System.Linq.Expressions.Expression.Equal
IL_005B: ldloc.2
IL_005C: ldc.i4.3
IL_005D: box System.Int32
IL_0062: ldtoken System.Int32
IL_0067: call System.Type.GetTypeFromHandle
IL_006C: call System.Linq.Expressions.Expression.Constant
IL_0071: call System.Linq.Expressions.Expression.Modulo
IL_0076: ldc.i4.0
IL_0077: box System.Int32
IL_007C: ldtoken System.Int32
IL_0081: call System.Type.GetTypeFromHandle
IL_0086: call System.Linq.Expressions.Expression.Constant
IL_008B: call System.Linq.Expressions.Expression.NotEqual
IL_0090: call System.Linq.Expressions.Expression.AndAlso
IL_0095: ldc.i4.1
IL_0096: newarr System.Linq.Expressions.ParameterExpression
IL_009B: stloc.3
IL_009C: ldloc.3
IL_009D: ldc.i4.0
IL_009E: ldloc.2
IL_009F: stelem.ref
IL_00A0: ldloc.3
IL_00A1: call System.Linq.Expressions.Expression.Lambda
IL_00A6: call System.Linq.Queryable.Where
IL_00AB: stloc.1
所以差异似乎是AsQuerable
版本正在构建表达式树,AsEnumerable
没有。