• Linq 用得太随意导致的性能问题一则


    问题场景

    有一个很多条数据的数据库(数据源),在其中找出指定的项,这些项的 ID 位于 给定的列表中,如 TargetList 中。

    private readonly IDictionary<string, int> _testSource = new Dictionary<string, int>();
    private readonly IList<string> _targetStringList = new List<string>();
    

    直观简洁的 Linq 写法

    public long TestFindByLinq()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        IList<int> theResultData = _testSource.Where(s => _targetStringList.Contains(s.Key)).Select(s=>s.Value).ToList();
        // IList<int> theResultData2 = _targetStringList.Select(f => _testSource[f]).ToList();
        sw.Stop();
        Console.WriteLine($"[TestFindByLinq] ElapsedTicks:{sw.ElapsedTicks}; TotalTargetCount:{_targetStringList.Count} 
    ");
        return sw.ElapsedTicks;
    }
    
    

    问题在于,如果这么写,将要遍历整个源数据,性能受影响。

    看起来麻烦,但性能好很多的写法

    public long TestFindByForEach()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        IList<int> theResultData = new List<int>();
        foreach (string target in _targetStringList)
        {
            int data = _testSource[target];
            theResultData.Add(data);
        }
        sw.Stop();
        Console.WriteLine($"[TestFindByForEach] ElapsedTicks:{sw.ElapsedTicks}; TotalTargetCount:{_targetStringList.Count}
    ");
        return sw.ElapsedTicks;
    }
    
    

    性能相差多少呢?

    从 100000 条数据中,找 6354 条数据。

    差不多是 1000 倍以上的性能差距。

    有什么启发吗?

    Linq 性能不好,有时候可能只是 Linq 写得不好,Linq 写起来很方便,但如果写法中涉及到的查询方式,需要遍历全部数据,性能必然受到影响,如果还要多次遍历全部内容,那就更可怕了。

    修正

    以上测试考虑问题确实是不全面的,详情可以看评论区大佬的回复。

    1 如果知道一定包含,可以用

    var testData = _targetStringList.Select(f => _testSource[f]).ToList();
    

    2 可以使用 hashSet

    var testData = _testSource.Where(s => _targetStringList.Contains(s.Key)).ToList();
    // 在原有的基础上,改动最小可以用HashSet
    var hashSet = _targetStringList.ToHashSet();
    var testData = _testSource.Where(s => hashSet.Contains(s.Key)).ToList();
    // by 评论区 @玩命夜狼 
    

    感谢各位大佬指正。


    原文链接 : https://www.cnblogs.com/jasongrass/p/10797795.html

    附:测试完整源码

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    
    namespace GrassDemoPark.CoreConsoleApp
    {
        class LinqTest
        {
            private readonly IDictionary<string, int> _testSource = new Dictionary<string, int>();
            private readonly IList<string> _targetStringList = new List<string>();
    
            public LinqTest()
            {
                for (int i = 0; i < 100000; i++)
                {
                    string guid = Guid.NewGuid().ToString();
                    _testSource.Add(guid, i);
    
                    if (guid.StartsWith("5"))
                    {
                        _targetStringList.Add(guid);
                    }
                }
            }
    
            public long TestFindByLinq()
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                IList<int> theResultData = _testSource.Where(s => _targetStringList.Contains(s.Key)).Select(s=>s.Value).ToList();
                // IList<int> theResultData2 = _targetStringList.Select(f => _testSource[f]).ToList();
                sw.Stop();
                Console.WriteLine($"[TestFindByLinq] ElapsedTicks:{sw.ElapsedTicks}; TotalTargetCount:{_targetStringList.Count} 
    ");
                return sw.ElapsedTicks;
            }
    
            public long TestFindByForEach()
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                IList<int> theResultData = new List<int>();
                foreach (string target in _targetStringList)
                {
                    int data = _testSource[target];
                    theResultData.Add(data);
                }
                sw.Stop();
                Console.WriteLine($"[TestFindByForEach] ElapsedTicks:{sw.ElapsedTicks}; TotalTargetCount:{_targetStringList.Count}
    ");
                return sw.ElapsedTicks;
            }
    
        }
    }
    
  • 相关阅读:
    Numpy(0)
    C++(成员函数做友元)
    C++(类做友元)
    CUDA---Arrayfire---添加cuda kernel
    CUDA--Arrayfire--类型转换
    C++(友元)
    C++(const修饰成员函数)
    C++(空指针访问成员函数)
    第9章 整合前端
    第8章 离不开的数据库
  • 原文地址:https://www.cnblogs.com/jasongrass/p/10797795.html
Copyright © 2020-2023  润新知