2011-12-29 25 views
60

Tôi đang làm một số xét nghiệm thực hiện và nhận thấy rằng một biểu thức LINQ nhưTại sao LINQ .Where (vị ngữ). Đầu tiên() nhanh hơn .First (vị ngữ)?

result = list.First(f => f.Id == i).Property 

là chậm hơn so với

result = list.Where(f => f.Id == i).First().Property 

Điều này có vẻ phản trực quan. Tôi đã nghĩ rằng biểu thức đầu tiên sẽ nhanh hơn vì nó có thể dừng lặp lại trong danh sách ngay khi vị từ được thỏa mãn, trong khi tôi đã nghĩ rằng biểu thức .Where() có thể lặp qua toàn bộ danh sách trước khi gọi số .First() trên tập con kết quả. Ngay cả khi sau này không ngắn mạch nó không nên nhanh hơn bằng cách sử dụng đầu tiên trực tiếp, nhưng nó được.

Dưới đây là hai bài kiểm tra đơn vị thực sự đơn giản minh họa điều này. Khi được biên dịch với tối ưu hóa trên TestWhereAndFirst nhanh hơn khoảng 30% so với TestFirstOnly trên .Net và Silverlight 4. Tôi đã thử làm cho biến vị ngữ trả về nhiều kết quả hơn nhưng sự khác biệt về hiệu suất là như nhau.

Có thể giải thích tại sao .First(fn) chậm hơn .Where(fn).First() không? Tôi thấy kết quả trực quan tương tự với số lượt truy cập là .Count(fn) so với .Where(fn).Count().

private const int Range = 50000; 

private class Simple 
{ 
    public int Id { get; set; } 
    public int Value { get; set; } 
} 

[TestMethod()] 
public void TestFirstOnly() 
{ 
    List<Simple> list = new List<Simple>(Range); 
    for (int i = Range - 1; i >= 0; --i) 
    { 
     list.Add(new Simple { Id = i, Value = 10 }); 
    } 

    int result = 0; 
    for (int i = 0; i < Range; ++i) 
    { 
     result += list.First(f => f.Id == i).Value; 
    } 

    Assert.IsTrue(result > 0); 
} 

[TestMethod()] 
public void TestWhereAndFirst() 
{ 
    List<Simple> list = new List<Simple>(Range); 
    for (int i = Range - 1; i >= 0; --i) 
    { 
     list.Add(new Simple { Id = i, Value = 10 }); 
    } 

    int result = 0; 
    for (int i = 0; i < Range; ++i) 
    { 
     result += list.Where(f => f.Id == i).First().Value; 
    } 

    Assert.IsTrue(result > 0); 
} 
+4

Bạn định thời gian như thế nào? –

+3

Suy nghĩ ban đầu của bạn là sai mặc dù: LINQ làm tính toán lười biếng, vì vậy khi 'First()' được gọi là nó sẽ truy vấn (giá trị trả về) 'Where (...)' cho chỉ một trận đấu và không bao giờ yêu cầu khác. Vì vậy, cùng một số lượng chính xác các yếu tố sẽ được kiểm tra khi bạn gọi 'Đầu tiên (...) '(tức là trực tiếp với một biến vị ngữ). – Jon

+1

Tôi nhận được kết quả tương tự, '.Where(). First()' là 0,21 giây và '.irst()' là 0,37 giây. Đây là một danh sách đơn giản của 'int'. – Ryan

Trả lời

43

Tôi nhận được kết quả tương tự: trong đó + đầu tiên nhanh hơn lần đầu tiên.

Như Jon đã lưu ý, Linq sử dụng đánh giá lười biếng để hiệu suất nên (và) tương tự rộng rãi cho cả hai phương pháp.

Nhìn vào Reflector, Đầu tiên sử dụng vòng lặp foreach đơn giản để lặp qua bộ sưu tập nhưng ở đâu có nhiều trình vòng lặp chuyên biệt cho các loại bộ sưu tập khác nhau (mảng, danh sách, v.v.). Có lẽ đây là những gì mang lại lợi thế nhỏ ở đâu.

+0

Tôi biết về đánh giá lười biếng. Tôi đã nghĩ rằng với đánh giá hoàn hảo lười biếng đâu + Đầu tiên sẽ cung cấp cho chính xác hiệu suất tương tự như đầu tiên. Điều tra của bạn với Reflector rất hữu ích, cảm ơn. Điều này chắc chắn giải thích tại sao đầu tiên một mình là chậm hơn. Tôi tự hỏi tại sao Microsoft quyết định chỉ làm ở đâu sử dụng các trình vòng lặp chuyên dụng? – dazza

+0

@ user1120411: Có, nhưng trước tiên (với một biến vị ngữ) và ở đâu chọn làm cơ bản giống nhau theo những cách khác nhau, do đó hiệu suất khác nhau. – arx

+7

Nhưng nếu tôi là một nhà phát triển khung và chỉ thực hiện First (fn) nội bộ như trở về đâu (fn). Đầu tiên() nó sẽ hoạt động chính xác như việc triển khai hiện tại của First ngoại trừ nhanh hơn! Có vẻ như một giám sát xấu về phần của Microsoft. – dazza

Các vấn đề liên quan