C# 过滤数据的三种姿势:yield return、LINQ Where、ToList 全景性能对决

测试代码

[MemoryDiagnoser]
public class YieldBenchmark
{
    private readonly Consumer _consumer = new Consumer();

    [Params(100, 10000, 100000)]
    public int N;

    private int[] _intArray;
    private MyClass[] _classArray;
    private MyStruct[] _structArray;

    [GlobalSetup]
    public void Setup()
    {
        _intArray = Enumerable.Range(0, N).ToArray();
        _classArray = Enumerable.Range(0, N).Select(i => new MyClass { Value = i }).ToArray();
        _structArray = Enumerable.Range(0, N).Select(i => new MyStruct { Value = i }).ToArray();
    }


    // ==================== int ====================
    [Benchmark] public void Int_Yield() => YieldInt(_intArray).Consume(_consumer);
    [Benchmark] public void Int_IfYield() => IfYieldInt(_intArray).Consume(_consumer);
    [Benchmark] public List<int> Int_ToList() => YieldInt(_intArray).ToList();
    [Benchmark] public int[] Int_ToArray() => YieldInt(_intArray).ToArray();

    private static IEnumerable<int> YieldInt(int[] source)
    {
        return source.Where(x => x % 2 == 0);
    }

    private static IEnumerable<int> IfYieldInt(int[] source)
    {
        foreach (var item in source)
            if (item % 2 == 0)
                yield return item;
    }

    // ==================== class ====================
    [Benchmark] public void Class_Yield() => YieldClass(_classArray).Consume(_consumer);
    [Benchmark] public void Class_IfYield() => IfYieldClass(_classArray).Consume(_consumer);
    [Benchmark] public List<MyClass> Class_ToList() => YieldClass(_classArray).ToList();
    [Benchmark] public MyClass[] Class_ToArray() => YieldClass(_classArray).ToArray();

    private static IEnumerable<MyClass> YieldClass(MyClass[] source)
    {
        return source.Where(x => x.Value % 2 == 0);
    }

    private static IEnumerable<MyClass> IfYieldClass(MyClass[] source)
    {
        foreach (var item in source)
            if (item.Value % 2 == 0)
                yield return item;
    }

    // ==================== struct ====================
    [Benchmark] public void Struct_Yield() => YieldStruct(_structArray).Consume(_consumer);
    [Benchmark] public void Struct_IfYield() => IfYieldStruct(_structArray).Consume(_consumer);
    [Benchmark] public List<MyStruct> Struct_ToList() => YieldStruct(_structArray).ToList();
    [Benchmark] public MyStruct[] Struct_ToArray() => YieldStruct(_structArray).ToArray();

    private static IEnumerable<MyStruct> YieldStruct(MyStruct[] source)
    {
        return source.Where(x => x.Value % 2 == 0);
    }

    private static IEnumerable<MyStruct> IfYieldStruct(MyStruct[] source)
    {
        foreach (var item in source)
            if (item.Value % 2 == 0)
                yield return item;
    }
}

public class MyClass
{
    public int Value;
}

public struct MyStruct
{
    public int Value;
}

测试结果

MethodNMeanErrorStdDevMedianGen0Gen1Gen2Allocated
Int_Yield100173.4 ns2.64 ns2.47 ns173.3 ns0.0076--48 B
Int_IfYield100156.7 ns0.57 ns0.50 ns156.6 ns0.0088--56 B
Int_ToList100258.2 ns4.85 ns4.77 ns257.4 ns0.1106--696 B
Int_ToArray100277.2 ns5.26 ns5.16 ns278.3 ns0.1144--720 B
Class_Yield100423.1 ns8.19 ns8.41 ns420.5 ns0.0076--48 B
Class_IfYield100394.8 ns4.34 ns4.06 ns394.9 ns0.0100--64 B
Class_ToList100410.5 ns5.47 ns5.11 ns408.9 ns0.18980.0005-1192 B
Class_ToArray100492.8 ns2.29 ns1.79 ns492.7 ns0.1898--1192 B
Struct_Yield100218.6 ns3.82 ns4.09 ns218.3 ns0.0076--48 B
Struct_IfYield100190.5 ns1.33 ns1.04 ns190.9 ns0.0088--56 B
Struct_ToList100263.2 ns6.99 ns20.60 ns274.4 ns0.1109--696 B
Struct_ToArray100306.3 ns4.50 ns3.99 ns307.1 ns0.1144--720 B
Int_Yield1000016,310.8 ns244.84 ns229.02 ns16,303.4 ns---48 B
Int_IfYield1000014,991.2 ns278.83 ns273.85 ns15,040.4 ns---56 B
Int_ToList1000021,684.4 ns419.96 ns483.63 ns21,635.4 ns10.46751.1597-65888 B
Int_ToArray1000023,093.1 ns225.83 ns211.24 ns23,045.3 ns8.48390.5798-53440 B
Class_Yield1000039,518.1 ns786.32 ns772.27 ns39,381.6 ns---48 B
Class_IfYield1000038,522.0 ns246.33 ns230.41 ns38,476.6 ns---64 B
Class_ToList1000032,900.5 ns645.10 ns904.35 ns32,591.2 ns20.81303.4180-131408 B
Class_ToArray1000033,693.9 ns278.96 ns260.94 ns33,711.5 ns16.84571.5259-106224 B
Struct_Yield1000019,381.9 ns319.79 ns299.13 ns19,403.8 ns---48 B
Struct_IfYield1000014,652.0 ns16.89 ns14.10 ns14,653.5 ns---56 B
Struct_ToList1000019,771.0 ns188.52 ns167.12 ns19,717.3 ns10.46751.1597-65888 B
Struct_ToArray1000016,348.6 ns139.48 ns123.65 ns16,322.8 ns8.48390.5798-53440 B
Int_Yield100000145,367.8 ns161.72 ns151.27 ns145,342.5 ns---48 B
Int_IfYield100000134,651.7 ns1,063.77 ns995.06 ns134,108.5 ns---56 B
Int_ToList100000182,513.1 ns571.52 ns534.60 ns182,429.3 ns98.632878.369177.3926525216 B
Int_ToArray100000191,113.4 ns857.08 ns801.71 ns191,026.0 ns88.378970.800867.3828463287 B
Class_Yield100000393,067.4 ns1,592.34 ns1,411.57 ns392,486.7 ns---48 B
Class_IfYield100000386,997.1 ns1,470.07 ns1,375.11 ns386,845.2 ns---64 B
Class_ToList100000595,860.0 ns5,105.46 ns4,775.65 ns596,887.2 ns183.5938163.0859162.10941050004 B
Class_ToArray100000457,150.3 ns4,990.60 ns4,668.21 ns456,151.7 ns181.6406165.5273160.1563925998 B
Struct_Yield100000186,671.9 ns1,434.43 ns1,197.81 ns186,435.3 ns---48 B
Struct_IfYield100000172,955.8 ns2,730.39 ns2,554.01 ns172,108.0 ns---56 B
Struct_ToList100000241,428.5 ns2,556.80 ns2,391.63 ns240,950.8 ns97.900477.880976.9043525206 B
Struct_ToArray100000201,362.2 ns657.62 ns549.15 ns201,645.8 ns84.716867.138763.7207463252 B

总结

  1. list通常比array更占内存
  2. 使用LINQ Where,会生成迭代器对象迭代器对象(WhereArrayIterator),产生48B内存
  3. 手写循环yield return返回,会生成一个状态机,产生56B/64B(引用类型占8B)内存
  4. 大数据量用IEnumerable可以减少内存分配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值