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} \n");
    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}\n");
    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} \n");
            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}\n");
            return sw.ElapsedTicks;
        }

    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM