對《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:下面是原文代碼的截圖

 

 

 


免責聲明!

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



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