对《LINQ能不能用系列(一)数组筛选效率对比》中测试的几个问题

Stone_W 同学写了一篇《LINQ能不能用系列(一)数组筛选效率对比》

错误一:作为对比测试,测试数组应该为同一个,否则测试数据没有可比性

错误二:对比组中对List的使用不对,List默认创建的数组大小为4,每次增长为4,那么这里就会反复重新创建新的数组次数为log10000000次左右当然会比Linq慢很多

错误三:面对Linq接近0毫秒的处理能力,稍微有点经验的同学就知道这是不可能的,除非是很强很强的计算机,至于为什么后面给出答案,总之linq查询里肯定有猫腻,直接调用tolist()强制返回结果再说;//这里Stone W在评论中对ToList有质疑,我之所以ToList是为了和第二组进行对比,因为第二组得到的结果是一个List,我很奇怪,这次的对比测试到底是为了测试得到两个结果集的算法对比呢还是测试Count算法的对比呢?如果是前者,一个拿到的是IEnumerable的对象一个是List对象,牛跟闹钟怎么对比哪个跑的快呢?也只有在调用ToList的时候才会真正执行Linq的算法也就是下面的嵌套类WhereListIterator;当然如果是为了进行Count对比的话那么对比组二中的算法真的有点拙劣,我想不会有谁会用方法二来统计。

下面是修改了如上三个明显错误后的代码,如果哪位同学有补充欢迎留言:

复制代码
        [Fact]
        public void LinqTest()
        {
            TestLinq(1);
            TestLinq(2);
            TestLinq(3);
        }

        public void TestLinq(int time)
        {
            const int listCount = 10000000; // 数组长度
            Random random = new Random(); // 数据随机构建值

            // 数组构建 
            List<int> list1 = new List<int>();
            for (int i = 0; i < listCount; i++)
            {
                list1.Add(random.Next(10000));
            }

            // 效率测试内容:提取数组中数值大于的100的数组

            // LINQ 测试
            Stopwatch linq_Stopwatch = new Stopwatch();
            linq_Stopwatch.Start();

            var linqList = (from num in list1
                            where num > 100
                            select num).ToList();
            linq_Stopwatch.Stop();
            // 普通方式 测试
            Stopwatch before_Stopwatch = new Stopwatch();
            before_Stopwatch.Start();

            List<int> beforeList = new List<int>(10000000);
            for (int i = 0; i < list1.Count(); i++)
            {
                if (list1[i] > 100)
                    beforeList.Add(list1[i]);
            }
            before_Stopwatch.Stop();


            Console.WriteLine(
                String.Format("第{0}次测试,测试:{5}条数据。\n\r \t LINQ用时:{1}毫秒,筛选了{2}条数据。\n\r\t 普通用时:{3}毫秒,筛选了{4}条数据。\r\n",
                              time, linq_Stopwatch.ElapsedMilliseconds, linqList.Count(),
                              before_Stopwatch.ElapsedMilliseconds, beforeList.Count(), listCount));

        }
复制代码

测试结果:

第1次测试,测试:10000000条数据。
LINQ用时:448毫秒,筛选了9898832条数据。
普通用时:437毫秒,筛选了9898832条数据。


第2次测试,测试:10000000条数据。
LINQ用时:516毫秒,筛选了9899569条数据。
普通用时:460毫秒,筛选了9899569条数据。


第3次测试,测试:10000000条数据。
LINQ用时:608毫秒,筛选了9899231条数据。
普通用时:470毫秒,筛选了9899231条数据。

 

结论:Linq在实现灵活性提高编写效率的时候牺牲了一定的性能,当然这个是必须的,有的必有失嘛。

我的选择:绝大部分时候使用Linq,在对性能要求高的时候使用普通的迭代;

 

 0毫秒的秘密:

var linqList = (from num in list1 where num > 100 select num)

先看看这个LinqList的类型(Console.WriteLine(linqList.GetType().FullName);):System.Linq.Enumerable+WhereListIterator`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

可以看到这是一个嵌套类,作用是对Where 条件进行迭代操作,贴上它的源代码:

class WhereListIterator<TSource> : Iterator<TSource>
{
    List<TSource> source;
    Func<TSource, bool> predicate;
    List<TSource>.Enumerator enumerator;
 
    public WhereListIterator(List<TSource> source, Func<TSource, bool> predicate) {
        this.source = source;
        this.predicate = predicate;
    }
 
    public override Iterator<TSource> Clone() {
        return new WhereListIterator<TSource>(source, predicate);
    }
 
    public override bool MoveNext() {
        switch (state) {
            case 1:
                enumerator = source.GetEnumerator();
                state = 2;
                goto case 2;
            case 2:
                while (enumerator.MoveNext()) {
                    TSource item = enumerator.Current;
                    if (predicate(item)) {
                        current = item;
                        return true;
                    }
                }
                Dispose();
                break;
        }
        return false;
    }
 
    public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) {
        return new WhereSelectListIterator<TSource, TResult>(source, predicate, selector);
    }
 
    public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {
        return new WhereListIterator<TSource>(source, CombinePredicates(this.predicate, predicate));
    }
}

真相大白于天下。   

ps:下面是原文代码的截图

 

 

 

posted @   today4king  阅读(2896)  评论(10编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
历史上的今天:
2009-05-08 页面生命周期
点击右上角即可分享
微信分享提示
主题色彩