Liệu thực sự Linq có nhanh hơn vòng lặp for,foreach, while

Bài viết trước tôi có nêu vấn đề so sánh giữa 2 mảng mà không sử dụng vòng lặp và thay vào đó là sử dụng Linq. Và nhiều bạn cũng đã hỏi vậy tốc độ xử lý giữa dùng vòng lặp và Linq thì dùng cái nào nhanh hơn.

Thực ra vấn đề so sánh tốc độ giữa Linq và vòng lặp không còn là chủ đề mới. Các bạn có thể search trên google là ra cả đống.

Có thể thấy là có nhiều kết quả trên google cho vấn đề này.

Tuy nhiên thực sự nhiều câu trả lời cũng chưa thỏa đáng. Chính vì vậy tôi đã làm bài viết này với những kiểm chứng và chính các bạn cũng có thể tự trải nghiệm được. 

Nếu như chỉ thực hiện vòng lặp một cách thuần túy. Tôi cam đoan rằng vòng lặp forforeach chắc chắn là nhanh hơn Linq. Và đây là ví dụ minh chứng.

1. Kiểm nghiệm tính tổng số của một mảng
Thực hiện so sánh
Phương thức Kết quả tính tổng Thời gian thực hiện (ticks)
Sử dụng vòng lặp for
Sử dụng vòng lặp foreach
Sử dụng lệnh Join của Linq
Đây là code mà tôi thực hiện kiểm chứng
private long OnStopWatch(Action action)
{
    var st = new Stopwatch();
    st.Start();
    action();
    st.Stop();
    return st.ElapsedTicks;
}

private void CreateListRandom(int totalNumber, List<int> list1)
{
    for (var i = 1; i <= totalNumber; i++)
    {
        list1.Add(Singleton<Random>.Inst.Next(0, 100));                
    }
}

public void Test1(int totalNumber)
{
    if (totalNumber > 50000) throw new Exception("Vui lòng chọn số lượng mảng nhỏ hơn 50000");

    var list = new List<int> { };
    CreateListRandom(totalNumber, list);

    var sumByFor = 0;
    var t1 = OnStopWatch(() =>
    {
        for (var i = 0; i < list.Count; i++)
        {
            sumByFor += list[i];
        }
    });

    var sumByForEach = 0;
    var t2 = OnStopWatch(() =>
    {
        foreach (var item1 in list)
        {
            sumByForEach += item1;
        }
    });

    var sumByJoinLinq = 0;
    var t3 = OnStopWatch(() =>
    {
        sumByJoinLinq = list.Sum(item => item);
    });

    this.SetData("Result", new { t1, sumByFor, t2, sumByForEach, t3, sumByJoinLinq, list1 = list.JoinString(item => item, " ") });
}

Rõ ràng với vòng lặp đơn thuần thì for nhanh nhất, Linq là chậm nhất

Nhưng đấy là chúng ta mới chỉ thực hiện với một vòng lặp đơn thuần. Trong thực tế khi duyệt phần tử ta có thể phải sử dụng for, foreach, while lồng nhau thì sao? Ví dụ như sắp xếp hoặc như trong bài trước của tôi là đối sánh giữa 2 phần tử của mảng thì sẽ như thế nào

Dưới đây là kiểm chứng mà tôi tìm ra những phần tử có chung ở 2 mảng rồi thực hiện tính tổng các phần tử này.
2. Kiểm nghiệm tính tổng số phần tử chung giữa 2 mảng
Thực hiện so sánh
Phương thức Kết quả tính tổng Thời gian thực hiện (ticks)
Sử dụng vòng lặp for
Sử dụng vòng lặp foreach
Sử dụng lệnh Join của Linq

Các bạn có thể thấy điều gì không. Thực tế nếu dùng Linq lại cho thấy tốc độ nhanh hơn rất nhiều.

Dưới đây là code tôi thực hiện kiểm chứng
private void CreateListRandom(int totalNumber, List<int> list1, List<int> list2)
{
    for (var i = 1; i <= totalNumber; i++)
    {
        list1.Add(Singleton<Random>.Inst.Next(0, 100));
        list2.Add(Singleton<Random>.Inst.Next(0, 100));
    }
}

public void Test2(int totalNumber)
{
    if (totalNumber > 50000) throw new Exception("Vui lòng chọn số lượng mảng nhỏ hơn 50000");

    // Tạo 2 mảng ngẫu nhiên
    var list1 = new List<int> { };
    var list2 = new List<int> { };
    CreateListRandom(totalNumber, list1, list2);

    var sumByFor = 0;
    var t1 = OnStopWatch(() => 
    {
        for(var i = 0; i < list1.Count;i++)
        {
            var item1 = list1[i];
            for(var j = 0; j < list2.Count;j++)
            {
                var item2 = list2[j];
                if (item1 == item2)
                    sumByFor += item1;
            }
        }
    });

    var sumByForEach = 0;
    var t2 = OnStopWatch(() => 
    {
        foreach(var item1 in list1)
        {
            foreach(var item2 in list2)
            {
                if (item1 == item2)
                    sumByForEach += item1;
            }
        }
    });

    var sumByJoinLinq = 0;
    var t3 = OnStopWatch(() => 
    {
        list1.Join(list2, item1 => item1, item2 => item2, (item1, item2) => sumByJoinLinq += item1).Count();
    });

    this.SetData("Result", new { t1, sumByFor, t2, sumByForEach, t3, sumByJoinLinq, list1 = list1.JoinString(item => item, " "), list2 = list2.JoinString(item => item, " ") });
}

Tôi cũng đã tìm tới Source code của thư viện Enumerable.cs của Microsoft để tìm hiểu nội dung hàm Join

public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector) 
{
    if (outer == null) throw Error.ArgumentNull("outer");
    if (inner == null) throw Error.ArgumentNull("inner");
    if (outerKeySelector == null) throw Error.ArgumentNull("outerKeySelector");
    if (innerKeySelector == null) throw Error.ArgumentNull("innerKeySelector");
    if (resultSelector == null) throw Error.ArgumentNull("resultSelector");
    return JoinIterator<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, null);
}

static IEnumerable<TResult> JoinIterator<TOuter, TInner, TKey, TResult>(IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer) 
{
    Lookup<TKey, TInner> lookup = Lookup<TKey, TInner>.CreateForJoin(inner, innerKeySelector, comparer);
    foreach (TOuter item in outer) 
    {
        Lookup<TKey, TInner>.Grouping g = lookup.GetGrouping(outerKeySelector(item), false);
        if (g != null) 
        {
            for (int i = 0; i < g.count; i++) 
            {
                yield return resultSelector(item, g.elements[i]);
            }
        }
    }
}

internal static Lookup<TKey, TElement> CreateForJoin(IEnumerable<TElement> source, Func<TElement, TKey> keySelector, IEqualityComparer<TKey> comparer) 
{
    Lookup<TKey, TElement> lookup = new Lookup<TKey, TElement>(comparer);
    foreach (TElement item in source) 
    {
        TKey key = keySelector(item);
        if (key != null) lookup.GetGrouping(key, true).Add(item);
    }
    return lookup;
}

internal Grouping GetGrouping(TKey key, bool create) 
{
    int hashCode = InternalGetHashCode(key);
    for (Grouping g = groupings[hashCode % groupings.Length]; g != null; g = g.hashNext)
        if (g.hashCode == hashCode && comparer.Equals(g.key, key)) return g;
    if (create) 
    {
        if (count == groupings.Length) Resize();
        int index = hashCode % groupings.Length;
        Grouping g = new Grouping();
        g.key = key;
        g.hashCode = hashCode;
        g.elements = new TElement[1];
        g.hashNext = groupings[index];
        groupings[index] = g;
        if (lastGrouping == null) 
        {
            g.next = g;
        }
        else 
        {
            g.next = lastGrouping.next;
            lastGrouping.next = g;
        }
        lastGrouping = g;
        count++;
        return g;
    }
    return null;
}

Đù. Code nó nhiều foreach, for vãi cả đ* mà méo hiểu sao nó nhanh thế nhỉ. Có lẽ có vấn đề gì đó. Hoặc có một thuật toán nào đó mà mình cũng chưa tìm hiểu tới. Nhưng phần nào cũng trả lời cho mọi người thấy là dùng Linq đúng chỗ sẽ tạo hiệu quả rất đáng ngạc nhiên. Và việc tôi dùng Linq để đối sánh giữa 2 mảng cũng rất là hợp lý. Hiện tại trong code mình cũng đã chuyển dần hết sang sử dụng Linq. Tuy nhiên cũng ko phải hoàn toàn. Trường hợp cần sử dụng for thì vẫn phải sử dụng. 

Nếu bạn có ý kiến gì hay lý giải được tai sao một số trường hợp như dùng Join lại nhanh hơn for thì hãy chia sẻ để cùng trao đổi. Chúc cuối tuần vui vẻ

Sơn 20

Nếu bạn thấy nội dung chia sẻ này có ích với bạn hãy Donate để tạo động lực cho tôi viết các bài viết tiếp theo nhé. Cảm ơn nhiều !!!!

Bài viết cùng chuyên mục

Hướng dẫn gửi email bằng code C#. Cấu hình tài khoản gmail để gửi email từ phần mềm.

Sử dụng thư viện SmtpClient để gửi email. Cấu hình tài khoản gmail ở trạng thái kém bảo mật để có thể gửi email từ phần mềm.

02/12/2020 Xem chi tiết
Các hướng đi cho lập trình viên khi lựa chọn ngôn ngữ lập trình.

Mình thì lựa chọn phát triển web nên đã chọn chuyên sâu về HTML, Css, Javascript, ASP.NET, SQL

23/11/2020 Xem chi tiết
Mật khẩu kém bảo mật được sử dụng phổ biến trong năm 2020

Bất ngờ với mật khẩu 123456 lại đứng top đầu danh sách. Cứ bảo sao tài khoản hay bị hack. Đúng là sai lầm và chủ quan.

20/11/2020 Xem chi tiết
Xây dựng khung phần mềm trên nền Web giao diện Desktop Window

Đã xây dựng gần như xong khung, chỉ việc viết module business để phát triển phần mềm trên nền tảng Web giao diện Window.

30/10/2020 Xem chi tiết
Tôi đã thiết kế ra design pattern Trứng có trước hay Gà có trước như thế nào

Về ý tưởng này tôi đã thực hiện khoảng 5,6 năm trước do tham gia vào dự án mà xây dựng các khối độc lập nhưng lại có tính năng tương tự nhau

19/10/2020 Xem chi tiết
Chia sẻ một số hiệu ứng Hover hữu ích khi làm Frontend viết bằng css

Gồm các hiệu ứng Hover ZoomIn, Hover Fade, Hover Blur, Hover Zoomout, Hover Mask, Hover Over Text, Hover Fall, Hover Blurout

16/10/2020 Xem chi tiết

Bài viết khác

Tại sao code của tôi thường ngắn gọn như vậy

Khi bắt tay vào thực hiện các yêu cầu tôi thường phân tích kỹ và code trừu tượng.

15/09/2020 Xem chi tiết
Hướng dẫn sử dụng bootstrap tourist để làm trợ giúp cho phần mềm

Làm trợ giúp, hướng dẫn cho khách hàng sử dụng website một cách đơn giản nhất. Tôi cũng đã ứng dụng cho phần mềm của mình.

12/09/2020 Xem chi tiết
Hướng dẫn sử dụng Jquery plugin Lazy Load everything

Đây là plugin tôi chỉnh sửa lại từ thư viện jquery.lazyload.js để từ đó có thể lazy với mọi phần tử và không chỉ riêng cho sử dụng lazy image.

10/09/2020 Xem chi tiết
Thư viện Linq.js trong javascript

Thư viện này cung cấp đầy đủ các phương thức như .NET 4.0 và mở rộng thêm nhiều phương thức khác

10/09/2020 Xem chi tiết
Hướng dẫn sử dụng Jquery Plugin Hotkeys - Tạo phím nóng trên trình duyệt web

Thư viện của tác giả Tzury Bar Yochay được viết từ năm 2010

06/09/2020 Xem chi tiết
Hướng dẫn sử dụng Timer trong Javascript

Định nghĩa đối tượng Timer tương tự như trong WinForm C# để thực hiện task vụ theo chu kỳ bằng Javascript

01/09/2020 Xem chi tiết
{"nalias":"lieu-thuc-su-linq-co-nhanh-hon-vong-lap-forforeach-while","lang":"2","cattype":"0","catId":"3","UrlEngine":"UrlNewsEngine","site":"1"}