Trong .NET, vòng lặp nào chạy nhanh hơn, 'for' hoặc 'foreach'?


345

Trong C # / VB.NET / .NET, vòng lặp nào chạy nhanh hơn, forhay foreach?

Kể từ khi tôi đọc rằng một forvòng lặp hoạt động nhanh hơn một foreachvòng lặp cách đây rất lâu, tôi cho rằng nó đúng với tất cả các bộ sưu tập, bộ sưu tập chung, tất cả các mảng, v.v.

Tôi đã lùng sục Google và tìm thấy một vài bài viết, nhưng hầu hết trong số đó là không có kết luận (đọc bình luận về các bài viết) và kết thúc mở.

Điều gì sẽ là lý tưởng là có từng kịch bản được liệt kê và giải pháp tốt nhất cho cùng một.

Ví dụ (chỉ là một ví dụ về cách nó phải như vậy):

  1. để lặp lại một mảng gồm hơn 1000 chuỗi - fortốt hơnforeach
  2. để lặp lại IListcác chuỗi (không chung chung) - foreachtốt hơnfor

Một vài tài liệu tham khảo được tìm thấy trên web cho cùng:

  1. Bài viết cũ tuyệt vời của Emmanuel Schanzer
  2. Dự án Code FOREACH Vs. CHO
  3. Blog - Đến foreachhay không foreach, đó là câu hỏi
  4. Diễn đàn ASP.NET - NET 1.1 C # forvs.foreach

[Biên tập]

Ngoài khía cạnh dễ đọc của nó, tôi thực sự quan tâm đến sự thật và số liệu. Có những ứng dụng mà dặm tối ưu hóa hiệu năng cuối cùng bị vắt kiệt.


3
Sự khác biệt vẫn tồn tại. Các mảng đặc biệt nên nhanh như foreach, nhưng đối với mọi thứ khác, các vòng lặp đơn giản sẽ nhanh hơn. Tất nhiên, hầu hết thời gian, điều này sẽ không tạo ra sự khác biệt, và tất nhiên, về mặt lý thuyết, một trình biên dịch JIT thông minh có thể loại bỏ sự khác biệt.
jalf

3
Không có ngữ cảnh, tôi không thể biết chính xác những gì bạn đang làm, nhưng điều gì xảy ra khi bạn bắt gặp một mảng được lấp đầy một phần?
Chris Cudmore

6
Nhân tiện, 2 triệu lượt truy cập / tháng không có gì đáng sợ. Trung bình ít hơn một lần nhấn mỗi giây.
Mehrdad Afshari

37
Lưu ý quan trọng : Câu hỏi này đã được hợp nhất ngày hôm qua với một câu hỏi hoàn toàn không liên quan về việc bị buộc phải sử dụng foreachthay vì fortrong C #. Nếu bạn thấy câu trả lời ở đây không có ý nghĩa gì cả, đó là lý do tại sao. Đổ lỗi cho người điều hành, không phải là câu trả lời không hay.
TED

7
@TED ​​Oh Tôi đã tự hỏi tất cả các ý kiến ​​"ông chủ của bạn là một thằng ngốc" đến từ đâu, cảm ơn
Gaspa79

Câu trả lời:


350

Patrick Smacchia viết blog về tháng này, với các kết luận sau:

  • đối với các vòng lặp trong Danh sách rẻ hơn một chút so với các vòng lặp foreach trong Danh sách.
  • Vòng lặp trên mảng rẻ hơn khoảng 2 lần so với lặp trên Danh sách.
  • Kết quả là, lặp trên mảng sử dụng for rẻ hơn 5 lần so với lặp trên Danh sách bằng cách sử dụng foreach (mà tôi tin rằng, là tất cả những gì chúng ta làm).

130
Tuy nhiên, đừng bao giờ quên: "Tối ưu hóa sớm là gốc rễ của mọi tội lỗi".
Oorang

18
@Hardwareguy: Một khi bạn biết rằng gần như nhanh hơn không thể nhận ra, tại sao bạn không nên bắt đầu sử dụng nó nói chung? Nó không mất thêm thời gian.
DevinB

47
@devinb, sử dụng "for" khó hơn sử dụng "foreach" vì nó thêm mã, một biến khác, một điều kiện bạn cần kiểm tra, v.v ... Đã bao nhiêu lần bạn thấy một lỗi trong vòng lặp "foreach" ?
tster

35
@Hardwareguy, hãy để tôi xem nếu tôi nói thẳng điều này. Mất nhiều thời gian hơn 5 lần để lặp qua một danh sách foreachso với việc lặp qua một mảng với forvà bạn đang gọi nó không đáng kể? Đó là một sự khác biệt về hiệu năng có thể quan trọng đối với ứng dụng của bạn và có thể không, nhưng tôi sẽ không loại bỏ nó ra khỏi tầm tay.
Robert Harvey

44
Đọc qua bài đăng trên blog, có vẻ như các bài kiểm tra đã được chạy trong Debug và không phát hành để có thể có một yếu tố. Ngoài ra, sự khác biệt là đặc biệt chỉ cho chi phí vòng lặp. Nó hoàn toàn không ảnh hưởng đến thời gian thực hiện phần thân của vòng lặp, hầu hết các trường hợp dài hơn nhiều so với thời gian cần thiết để chuyển sang phần tử tiếp theo của danh sách. Đó là thông tin tốt để biết khi bạn xác định rõ ràng có vấn đề và bạn đã đo lường sự khác biệt trong ứng dụng của mình một cách cụ thể và có một sự cải thiện đáng chú ý nhưng chắc chắn không phải là lời khuyên chung để loại bỏ tất cả foreach.
Davy8

164

Đầu tiên, một yêu cầu phản tố đối với câu trả lời (hiện đã bị xóa) của Dmitry . Đối với các mảng, trình biên dịch C # phát ra phần lớn mã foreachgiống như đối với một forvòng lặp tương đương . Điều đó giải thích tại sao đối với điểm chuẩn này, kết quả về cơ bản là giống nhau:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 1000000;
    const int Iterations = 10000;

    static void Main()
    {
        double[] data = new double[Size];
        Random rng = new Random();
        for (int i=0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }

        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j=0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
    }
}

Các kết quả:

For loop: 16638
Foreach loop: 16529

Tiếp theo, xác nhận quan điểm của Greg về loại bộ sưu tập là quan trọng - thay đổi mảng thành một List<double>ở trên và bạn nhận được kết quả hoàn toàn khác nhau. Nói chung, không chỉ chậm hơn đáng kể, mà foreach trở nên chậm hơn đáng kể so với truy cập theo chỉ mục. Có nói rằng, tôi vẫn hầu như luôn thích tìm kiếm một vòng lặp for, nơi nó làm cho mã đơn giản hơn - bởi vì khả năng đọc hầu như luôn luôn quan trọng, trong khi tối ưu hóa vi mô hiếm khi xảy ra.


"Thay đổi mảng thành Danh sách <double> ở trên và bạn nhận được kết quả hoàn toàn khác nhau" Rất thú vị, tôi đã không nghĩ về điều đó
johnc

5
Do sự khác biệt kỳ lạ về kết quả giữa các bài kiểm tra của tôi và điểm chuẩn của người khác, tôi nghĩ rằng điều này sẽ xứng đáng với một bài đăng trên blog ...
Jon Skeet

1
Mà bạn hầu như luôn luôn thích giữa các mảng và List<T>? Có khả năng đọc tối ưu hóa vi mô trong trường hợp đó quá không?
JohnB

12
@ John: Yup - Tôi hầu như luôn thích List<T>hơn mảng. Các trường hợp ngoại lệ là char[]byte[]thường được coi là "khối" dữ liệu hơn là các bộ sưu tập thông thường.
Jon Skeet

Thật tuyệt vời, tôi thậm chí còn nhận được sự khác biệt mạnh mẽ hơn trên máy tính của mình, gần 10% ủng hộ việc nghiên cứu về các mảng đơn giản. Tôi đoán một cách điên cuồng rằng tất cả điều này bắt nguồn từ jitter không phải lo lắng về biến phụ, kiểm tra ràng buộc, vv .. sẽ rất thú vị nếu ai đó có một lời giải thích sâu sắc về điều này.
Tamir Daniely

162

foreachcác vòng lặp thể hiện ý định cụ thể hơn forcác vòng lặp .

Việc sử dụng một foreachvòng lặp chứng minh cho bất kỳ ai sử dụng mã của bạn rằng bạn đang dự định làm điều gì đó cho từng thành viên của bộ sưu tập bất kể vị trí của nó trong bộ sưu tập. Nó cũng cho thấy bạn không sửa đổi bộ sưu tập gốc (và ném ngoại lệ nếu bạn cố gắng).

Ưu điểm khác của foreachnó là nó hoạt động trên bất kỳ IEnumerable, trong đó forchỉ có ý nghĩa đối với IList, trong đó mỗi phần tử thực sự có một chỉ mục.

Tuy nhiên, nếu bạn cần sử dụng chỉ mục của một yếu tố, thì tất nhiên bạn nên được phép sử dụng một forvòng lặp. Nhưng nếu bạn không cần sử dụng một chỉ mục, có một chỉ là làm lộn xộn mã của bạn.

Không có ý nghĩa hiệu suất đáng kể như tôi biết. Ở giai đoạn nào đó trong tương lai có thể dễ dàng điều chỉnh mã bằng cách foreachchạy trên nhiều lõi, nhưng đó không phải là điều đáng lo ngại ngay bây giờ.


23
ctford: Không, không phải vậy. Trình biên dịch chắc chắn không thể sắp xếp lại các phần tử trong foreach. foreachhoàn toàn không liên quan đến lập trình chức năng. Đó hoàn toàn là một mô hình bắt buộc của lập trình. Bạn đang quy kết sai những điều xảy ra trong TPL và PLINQ foreach.
Mehrdad Afshari

13
@BlueTrin: Nó chắc chắn đảm bảo việc đặt hàng (phần C # spec 8.8.4 chính thức được định nghĩa foreachlà tương đương với một whilevòng lặp). Tôi nghĩ rằng tôi biết @ctford đang đề cập đến. Thư viện song song tác vụ cho phép bộ sưu tập cơ bản cung cấp các phần tử theo thứ tự tùy ý (bằng cách gọi .AsParallelvào một số liệt kê). foreachkhông làm gì ở đây và phần thân của vòng lặp được thực thi trên một luồng . Điều duy nhất được song song là việc tạo ra chuỗi.
Mehrdad Afshari

6
Enumerable.Select có một tình trạng quá tải cho phép bạn có được chỉ mục của mục, do đó, ngay cả nhu cầu về chỉ mục cũng không bắt buộc sử dụng cho. Xem msdn.microsoft.com/en-us/l
Library / bb534869.aspx

5
ForEach tiện dụng cho khả năng đọc và lưu gõ. Chi phí quan trọng, mặc dù; thay đổi 2 hoặc 3 vòng lặp trong giao diện người dùng thiết kế tài liệu mà tôi đã thực hiện, từ (cho mỗi obj trong danh sách) thành (cho i = 0 thành list.count-1) giảm thời gian phản hồi từ 2-3 giây mỗi lần chỉnh sửa xuống còn khoảng. 5 giây cho mỗi lần chỉnh sửa trên một tài liệu nhỏ chỉ cần lặp vài trăm đối tượng. Bây giờ đối với các tài liệu thậm chí rất lớn, không có sự gia tăng thời gian để lặp lại tất cả. Tôi không có ý kiến ​​nào về chuyện này xảy ra. Những gì tôi biết là sự thay thế là một sơ đồ phức tạp để chỉ lặp một tập hợp con của các đối tượng. Tôi sẽ thay đổi 5 phút mỗi ngày! - không tối ưu hóa vi mô.
FastAl

5
@FastAl Sự khác biệt giữa foreachforhiệu suất cho các danh sách thông thường là phân số của một giây để lặp lại hàng triệu mục để vấn đề của bạn chắc chắn không liên quan trực tiếp đến hiệu suất của mục đích, ít nhất là không phải cho vài trăm đối tượng. Âm thanh như một thực hiện liệt kê bị hỏng trong bất kỳ danh sách bạn đang sử dụng.
Mike Marynowski

53

Bất cứ khi nào có tranh luận về hiệu suất, bạn chỉ cần viết một bài kiểm tra nhỏ để bạn có thể sử dụng kết quả định lượng để hỗ trợ cho trường hợp của mình.

Sử dụng lớp StopWatch và lặp lại vài thứ một vài lần, cho chính xác. (Điều này có thể khó nếu không có vòng lặp for):

using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
    //do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds

Các ngón tay vượt qua kết quả của điều này cho thấy sự khác biệt là không đáng kể và bạn cũng có thể làm bất cứ điều gì dẫn đến mã duy trì nhất


13
Nhưng bạn không thể so sánh hiệu suất của for và foreach. Chúng được cho là được sử dụng trong các trường hợp khác nhau.
Michael Krelin - hacker

7
Tôi đồng ý với bạn Michael, bạn không nên chọn cái nào sẽ sử dụng dựa trên hiệu suất - bạn nên chọn cái nào có ý nghĩa nhất! Nhưng nếu sếp của bạn nói "Đừng sử dụng vì nó chậm hơn so với thuyết minh" thì đây là cách duy nhất để thuyết phục anh ta rằng sự khác biệt là không đáng kể
Rob Fonseca-Ensor

"(Điều này có thể khó nếu không có vòng lặp for)" Hoặc bạn có thể sử dụng vòng lặp while.
jonescb

49

Nó sẽ luôn luôn gần gũi. Đối với một mảng, đôi khi for nhanh hơn một chút, nhưng foreachbiểu cảm hơn và cung cấp LINQ, v.v. Nói chung, hãy gắn bó foreach.

Ngoài ra, foreachcó thể được tối ưu hóa trong một số tình huống. Ví dụ, một danh sách được liên kết có thể là khủng khiếp bởi người lập chỉ mục, nhưng nó có thể nhanh chóng bằng foreach. Trên thực tế, tiêu chuẩn LinkedList<T>thậm chí không cung cấp một bộ chỉ mục vì lý do này.


Vì vậy, bạn đang nói LinkedList<T>là gầy hơn List<T>? Và nếu tôi luôn luôn sử dụng foreach(thay vì for), tôi có nên sử dụng LinkedList<T>không?
JohnB

2
@JohnB - không gầy hơn; chỉ khác Ví dụ: mỗi nút trong danh sách được liên kết có các tham chiếu bổ sung không cần thiết cho một mảng phẳng (cũng là nền tảng List<T>). Nó là nhiều hơn mà rẻ hơn để chèn / loại bỏ .
Marc Gravell

36

Tôi đoán là nó có thể sẽ không có ý nghĩa trong 99% các trường hợp, vậy tại sao bạn lại chọn nhanh hơn thay vì phù hợp nhất (như dễ hiểu / duy trì nhất)?


6
@klew, Nếu bạn thực sự lập hồ sơ mã của mình, bạn sẽ không phải đoán 20% nào cần phải nhanh nhất có thể. Bạn có thể cũng sẽ phát hiện ra rằng số vòng lặp thực sự cần phải nhanh hơn thấp hơn nhiều. Hơn nữa, bạn có thực sự nói rằng hành động lặp là nơi bạn dành thời gian, trái ngược với những gì bạn thực sự làm trong vòng lặp đó?
tster

32

Không có khả năng có một sự khác biệt lớn về hiệu suất giữa hai. Như mọi khi, khi phải đối mặt với "cái nào nhanh hơn?" Câu hỏi, bạn nên luôn luôn nghĩ "Tôi có thể đo lường điều này."

Viết hai vòng lặp làm cùng một thứ trong phần thân của vòng lặp, thực hiện và định thời gian cho cả hai và xem sự khác biệt về tốc độ là gì. Làm điều này với cả một cơ thể gần như trống rỗng và một cơ thể vòng lặp tương tự như những gì bạn thực sự sẽ làm. Ngoài ra, hãy thử với loại bộ sưu tập mà bạn đang sử dụng, bởi vì các loại bộ sưu tập khác nhau có thể có các đặc tính hiệu suất khác nhau.


32

Có những lý do rất tốt để thích foreach vòng lặp hơn forvòng lặp. Nếu bạn có thể sử dụng một foreachvòng lặp, ông chủ của bạn là đúng mà bạn nên.

Tuy nhiên, không phải mỗi lần lặp chỉ đơn giản là đi qua một danh sách theo thứ tự từng cái một. Nếu anh ta cấm đoán , vâng đó là sai.

Nếu tôi là bạn, những gì tôi sẽ làm là biến tất cả các vòng lặp tự nhiên của bạn thành đệ quy . Điều đó dạy cho anh ấy, và đó cũng là một bài tập tinh thần tốt cho bạn.


Làm thế nào để đệ quy so với forvòng lặp và foreachvòng lặp hiệu suất khôn ngoan?
JohnB

Nó phụ thuộc. Nếu bạn sử dụng đệ quy đuôi và trình biên dịch của bạn đủ thông minh để chú ý, nó có thể giống hệt nhau. OTOH: Nếu không và bạn làm điều gì đó ngu ngốc như truyền nhiều dữ liệu không chính thức (không thay đổi) làm tham số hoặc khai báo các cấu trúc lớn trên ngăn xếp dưới dạng cục bộ, thì nó có thể thực sự rất chậm (hoặc thậm chí hết RAM).
TED

À Tôi thấy tại sao bạn hỏi điều đó bây giờ. Câu trả lời này đã đi đến một câu hỏi hoàn toàn khác. Đối với một số lý do bizzare Jonathan Sampson sáp nhập hai ngày hôm qua. Anh ấy thực sự không nên làm điều đó. Các câu trả lời hợp nhất sẽ không có ý nghĩa gì ở đây.
TED

18

Jeffrey Richter trên TechEd 2005:

"Tôi đã đến để học hỏi trong nhiều năm qua, trình biên dịch C # về cơ bản là một kẻ nói dối với tôi." .. "Nó nói dối về nhiều thứ." .. "Giống như khi bạn thực hiện một vòng lặp foreach ..." .. "... đó là một dòng mã nhỏ mà bạn viết, nhưng trình biên dịch C # đã tạo ra để làm điều đó thật phi thường. Nó đưa ra một thử / cuối cùng chặn ở đó, bên trong khối cuối cùng, nó biến biến của bạn thành giao diện IDis Dùng một lần và nếu cast thành công, nó gọi phương thức Dispose trên nó, bên trong vòng lặp, nó gọi thuộc tính Hiện tại và phương thức MoveNext lặp lại bên trong vòng lặp, Các đối tượng đang được tạo ra bên dưới vỏ bọc. Rất nhiều người sử dụng foreach vì nó rất dễ mã hóa, rất dễ thực hiện .. ".." foreach không tốt lắm về mặt hiệu suất,

Webcast theo yêu cầu: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCarget=3&cocate=en-US&CountryCode=US


12

Chuyện này thật vớ vẩn. Không có lý do thuyết phục nào để cấm vòng lặp for, vòng lặp hiệu năng hoặc khác.

Xem blog của Jon Skeet để biết điểm chuẩn hiệu suất và các đối số khác.


2
Liên kết được cập nhật: codeblog.jonskeet.uk/2009/01/29/ Quảng cáo
Matt

Cấu trúc vòng lặp nhanh hơn phụ thuộc vào những gì bạn cần lặp đi lặp lại. Một blog khác đánh giá nhiều lần lặp lại trên nhiều loại đối tượng , chẳng hạn như DataRows và các đối tượng tùy chỉnh. Nó cũng bao gồm hiệu năng của cấu trúc vòng lặp While và không chỉ các cấu trúc vòng lặp for và foreach.
miễn phí ngày 24

11

Trong trường hợp bạn làm việc với một bộ sưu tập các đối tượng, foreachthì tốt hơn, nhưng nếu bạn tăng một số, một forvòng lặp sẽ tốt hơn.

Lưu ý rằng trong trường hợp cuối cùng, bạn có thể làm một cái gì đó như:

foreach (int i in Enumerable.Range(1, 10))...

Nhưng nó chắc chắn không hoạt động tốt hơn, nó thực sự có hiệu suất kém hơn so với a for.


"Tốt hơn" là có thể gỡ lỗi: Nó chậm hơn và trình gỡ lỗi dnspy ​​sẽ không xâm nhập vào một foreach C # (mặc dù trình gỡ lỗi VS2017 sẽ). Đôi khi dễ đọc hơn nhưng nếu bạn hỗ trợ các ngôn ngữ mà không có nó, nó có thể gây phiền nhiễu.
Zeek2

10

Điều này sẽ giúp bạn tiết kiệm:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

Sử dụng:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

Để giành chiến thắng lớn hơn, bạn có thể lấy ba đại biểu làm tham số.


1
Một sự khác biệt nhỏ là một forvòng lặp thường được viết để loại trừ phần cuối của phạm vi (ví dụ 0 <= i < 10). Parallel.Forcũng làm điều đó để giữ cho nó dễ dàng thay thế với một forvòng lặp chung .
Groo

9

bạn có thể đọc về nó trong Deep .NET - phần 1 Lặp lại

nó bao gồm các kết quả (không có khởi tạo đầu tiên) từ mã nguồn .NET cho đến khi tháo gỡ.

ví dụ - Lặp lại mảng với vòng lặp foreach: nhập mô tả hình ảnh ở đây

và - liệt kê lặp với vòng lặp foreach: nhập mô tả hình ảnh ở đây

và kết quả cuối cùng: nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây


8

Sự khác biệt về tốc độ trong một for- và một foreach-loop là rất nhỏ khi bạn lặp qua các cấu trúc phổ biến như mảng, danh sách, v.v.LINQ truy vấn trên bộ sưu tập hầu như luôn chậm hơn một chút, mặc dù nó dễ viết hơn! Như các áp phích khác đã nói, hãy thể hiện sự biểu cảm hơn là một phần nghìn giây của hiệu suất bổ sung.

Điều chưa được nói cho đến nay là khi một foreachvòng lặp được biên dịch, nó được tối ưu hóa bởi trình biên dịch dựa trên bộ sưu tập mà nó đang lặp lại. Điều đó có nghĩa là khi bạn không chắc chắn nên sử dụng vòng lặp nào, bạn nên sử dụngforeach vòng lặp nào vòng lặp - nó sẽ tạo ra vòng lặp tốt nhất cho bạn khi nó được biên dịch. Nó cũng dễ đọc hơn.

Một lợi thế quan trọng khác với foreachvòng lặp là nếu việc triển khai bộ sưu tập của bạn thay đổi (từ int arraythành List<int>ví dụ) thì foreachvòng lặp của bạn sẽ không yêu cầu bất kỳ thay đổi mã nào:

foreach (int i in myCollection)

Trên đây là như nhau cho dù bộ sưu tập của bạn thuộc loại nào, trong khi trong forvòng lặp của bạn , phần sau sẽ không được xây dựng nếu bạn thay đổi myCollectiontừ một arraythành List:

for (int i = 0; i < myCollection.Length, i++)

7

"Có bất kỳ đối số nào tôi có thể sử dụng để giúp tôi thuyết phục anh ấy vòng lặp for được chấp nhận để sử dụng không?"

Không, nếu sếp của bạn đang quản lý vi mô đến mức cho bạn biết ngôn ngữ lập trình nào sẽ được sử dụng, thì thực sự bạn không thể nói gì. Lấy làm tiếc.


7

Nó có thể phụ thuộc vào loại bộ sưu tập bạn đang liệt kê và việc thực hiện bộ chỉ mục của nó. Nhìn chung, sử dụng foreachcó thể là một cách tiếp cận tốt hơn.

Ngoài ra, nó sẽ hoạt động với bất kỳ IEnumerable- không chỉ những thứ với người lập chỉ mục.


7

Câu hỏi này có hai câu trả lời giống như hầu hết các câu hỏi "nhanh hơn":

1) Nếu bạn không đo lường, bạn không biết.

2) (Bởi vì ...) Nó phụ thuộc.

Nó phụ thuộc vào mức độ đắt của phương thức "MoveNext ()", liên quan đến mức độ đắt của phương thức "this [int index]", đối với loại (hoặc loại) của IEnumerable mà bạn sẽ lặp đi lặp lại.

Từ khóa "foreach" là viết tắt của một loạt các hoạt động - nó gọi GetEnumerator () một lần trên IEnumerable, nó gọi MoveNext () một lần mỗi lần lặp, nó thực hiện một số kiểu kiểm tra, v.v. Điều có khả năng ảnh hưởng nhất đến các phép đo hiệu suất là chi phí của MoveNext () vì điều đó được gọi O (N) lần. Có thể nó rẻ, nhưng có lẽ không.

Từ khóa "cho" có vẻ dễ dự đoán hơn, nhưng bên trong hầu hết các "vòng lặp" cho "bạn sẽ tìm thấy một cái gì đó như" bộ sưu tập [chỉ mục] ". Điều này trông giống như một hoạt động lập chỉ mục mảng đơn giản, nhưng thực sự là một cuộc gọi phương thức, mà chi phí của nó phụ thuộc hoàn toàn vào bản chất của bộ sưu tập mà bạn đang lặp đi lặp lại. Có lẽ nó rẻ, nhưng có lẽ không.

Nếu cấu trúc cơ bản của bộ sưu tập về cơ bản là một danh sách được liên kết, MoveNext rất rẻ, nhưng bộ chỉ mục có thể có chi phí O (N), làm cho chi phí thực sự của một vòng lặp "cho" O (N * N).


6

Mỗi ngôn ngữ xây dựng có một thời gian và địa điểm thích hợp để sử dụng. Có một lý do ngôn ngữ C # có bốn câu lệnh lặp riêng biệt - mỗi câu có một mục đích cụ thể và có cách sử dụng phù hợp.

Tôi khuyên bạn nên ngồi xuống với sếp của bạn và cố gắng giải thích một cách hợp lý lý do tại sao một forvòng lặp có mục đích. Đôi khi, một forkhối lặp mô tả rõ ràng hơn một thuật toán hơn là một foreachlần lặp. Khi điều này là đúng, nó là thích hợp để sử dụng chúng.

Tôi cũng chỉ ra cho sếp của bạn - Hiệu suất không phải và không phải là vấn đề theo bất kỳ cách thực tế nào - đó là vấn đề thể hiện thuật toán một cách cô đọng, có ý nghĩa, có thể duy trì được. Tối ưu hóa vi mô như thế này hoàn toàn bỏ lỡ điểm tối ưu hóa hiệu suất, vì bất kỳ lợi ích hiệu suất thực tế nào cũng sẽ đến từ thiết kế lại và tái cấu trúc thuật toán, chứ không phải tái cấu trúc vòng lặp.

Nếu, sau một cuộc thảo luận hợp lý, vẫn còn quan điểm độc đoán này, bạn phải làm thế nào để tiến hành. Cá nhân, tôi sẽ không vui khi làm việc trong một môi trường mà suy nghĩ hợp lý không được khuyến khích, và sẽ xem xét chuyển sang một vị trí khác dưới một chủ nhân khác. Tuy nhiên, tôi thực sự khuyên bạn nên thảo luận trước khi buồn bã - có thể chỉ có một sự hiểu lầm đơn giản.


5

Đó là những gì bạn làm bên trong vòng lặp ảnh hưởng đến sự hoàn hảo, chứ không phải cấu trúc vòng lặp thực tế (giả sử trường hợp của bạn là không tầm thường).


5

Cho dù forlà nhanh hơn foreachlà thực sự bên cạnh điểm. Tôi thực sự nghi ngờ rằng việc chọn cái này sẽ làm ảnh hưởng đáng kể đến hiệu suất của bạn.

Cách tốt nhất để tối ưu hóa ứng dụng của bạn là thông qua hồ sơ mã thực tế. Điều đó sẽ xác định chính xác các phương pháp chiếm nhiều công việc / thời gian nhất. Tối ưu hóa những người đầu tiên. Nếu hiệu suất vẫn không được chấp nhận, lặp lại quy trình.

Theo nguyên tắc chung, tôi khuyên bạn nên tránh xa tối ưu hóa vi mô vì chúng sẽ hiếm khi mang lại bất kỳ lợi ích đáng kể nào. Chỉ có ngoại lệ là khi tối ưu hóa các đường dẫn nóng được xác định (nghĩa là nếu hồ sơ của bạn xác định một vài phương thức được sử dụng nhiều, có thể có ý nghĩa để tối ưu hóa các đường dẫn rộng rãi này).


Nếu loại tối ưu hóa duy nhất tôi cần thực hiện trong các dự án tôi làm là tối ưu hóa vi mô, tôi sẽ là một người cắm trại hạnh phúc. Đáng buồn thay, điều này không bao giờ là trường hợp.
Yannick M Bông

2
forlà nhanh hơn một chút foreach. Tôi thực sự phản đối tuyên bố này. Điều đó hoàn toàn phụ thuộc vào bộ sưu tập cơ bản. Nếu một lớp danh sách được liên kết cung cấp một bộ chỉ mục với một tham số nguyên, tôi sẽ mong đợi sử dụng một forvòng lặp trên nó là O (n ^ 2) trong khi foreachdự kiến ​​là O (n).
Mehrdad Afshari

@Merhdad: Thật ra đó là một điểm tốt. Tôi chỉ nghĩ về trường hợp thường xuyên lập chỉ mục một danh sách (tức là mảng). Tôi sẽ tua lại để phản ánh điều đó. Cảm ơn.
Brian Rasmussen

@Mehrdad Afshari: indexing một bộ sưu tập của số nguyên có thể nhiều chậm hơn so với liệt kê trên nó. Nhưng bạn thực sự đang so sánh việc sử dụng for tra cứu chỉ mục với việc sử dụng foreachchính nó. Tôi nghĩ rằng câu trả lời của @Brian Rasmussen là chính xác rằng, ngoài bất kỳ việc sử dụng nào với bộ sưu tập, forsẽ luôn nhanh hơn một chút foreach. Tuy nhiên, forcộng với việc tra cứu bộ sưu tập sẽ luôn chậm hơn foreachchính nó.
Daniel Pryden

@Daniel: Hoặc là bạn có một mảng đơn giản, cả hai sẽ tạo mã giống hệt nhau hoặc có một bộ chỉ mục liên quan khi bạn sử dụng forcâu lệnh. Plain forvòng lặp với một biến kiểm soát số nguyên là không thể so sánh với foreach, vì vậy đó là ra. Tôi hiểu ý nghĩa của @Brian và nó đúng như bạn nói nhưng câu trả lời có thể gây hiểu nhầm. Re: Điểm cuối cùng của bạn: không có, trên thực tế, forhơn List<T>là vẫn nhanh hơn foreach.
Mehrdad Afshari

4

Cả hai sẽ chạy gần như chính xác theo cùng một cách. Viết một số mã để sử dụng cả hai, sau đó cho anh ta thấy IL. Nó sẽ hiển thị các tính toán tương đương, có nghĩa là không có sự khác biệt trong hiệu suất.


Trình biên dịch nhận ra các vòng lặp foreach được sử dụng trên các mảng / ILists, v.v. và thay đổi chúng thành các vòng lặp.
Callum Rogers

3
Chỉ cho anh ta những bằng chứng không thể hiểu được rằng nó ổn và yêu cầu bằng chứng của anh ta rằng nó không ổn.
cjk

3

vì có logic đơn giản hơn để thực hiện nên nó nhanh hơn foreach.


3

Trừ khi bạn đang trong một quy trình tối ưu hóa tốc độ cụ thể, tôi sẽ nói sử dụng phương pháp nào tạo ra mã dễ đọc và duy trì nhất.

Nếu một trình vòng lặp đã được thiết lập, giống như với một trong các lớp bộ sưu tập, thì foreach là một lựa chọn dễ dàng tốt. Và nếu đó là một phạm vi số nguyên mà bạn đang lặp đi lặp lại, thì có lẽ là sạch hơn.



3

Trong hầu hết các trường hợp thực sự không có sự khác biệt.

Thông thường, bạn luôn phải sử dụng foreach khi bạn không có chỉ số bằng số rõ ràng và bạn luôn phải sử dụng khi bạn thực sự không có bộ sưu tập lặp (ví dụ: lặp qua lưới mảng hai chiều trong tam giác trên) . Có một số trường hợp bạn có một sự lựa chọn.

Người ta có thể lập luận rằng đối với các vòng lặp có thể khó bảo trì hơn một chút nếu số ma thuật bắt đầu xuất hiện trong mã. Bạn nên có quyền bực mình vì không thể sử dụng vòng lặp for và phải xây dựng một bộ sưu tập hoặc sử dụng lambda để xây dựng một bộ sưu tập thay vì chỉ vì các vòng lặp đã bị cấm.


3

Có vẻ hơi lạ khi hoàn toàn cấm sử dụng một cái gì đó như vòng lặp for.

Có một bài viết thú vị ở đây bao gồm rất nhiều sự khác biệt về hiệu suất giữa hai vòng lặp.

Tôi sẽ nói cá nhân tôi thấy foreach dễ đọc hơn một chút đối với các vòng lặp nhưng bạn nên sử dụng tốt nhất cho công việc trong tay và không phải viết thêm mã dài để bao gồm một vòng lặp foreach nếu vòng lặp for phù hợp hơn.


Trích dẫn cần thiết từ bài viết mà bạn liên kết đến: "... nếu bạn dự định viết mã hiệu suất cao không dành cho các bộ sưu tập, hãy sử dụng cho vòng lặp. Ngay cả đối với các bộ sưu tập, foreach có thể trông hữu ích khi sử dụng, nhưng nó không hiệu quả."
NickFitz

3

Tôi tìm thấy foreachvòng lặp lặp đi lặp lại List nhanh hơn . Xem kết quả kiểm tra của tôi dưới đây. Trong đoạn mã dưới đây, tôi lặp lại một arraykích thước 100, 10000 và 100000 riêng biệt bằng cách sử dụng forforeachlặp để đo thời gian.

nhập mô tả hình ảnh ở đây

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

CẬP NHẬT

Sau khi đề xuất @jgauffin, tôi đã sử dụng mã @johnskeet và thấy rằng forvòng lặp với arraynhanh hơn sau,

  • Vòng lặp Foreach với mảng.
  • Đối với vòng lặp với danh sách.
  • Vòng lặp Foreach với danh sách.

Xem kết quả kiểm tra của tôi và mã dưới đây,

nhập mô tả hình ảnh ở đây

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }

3
Đây là một bài kiểm tra rất kém. a) bạn thực hiện quá ít lần lặp để có câu trả lời kết luận b) Chủ đề đó. Giấc ngủ sẽ không thực sự chờ đợi một phần nghìn giây. Sử dụng phương pháp tương tự như Jon Skeet đã làm trong câu trả lời của mình.
jgauffin

1
99,99% thời gian được sử dụng chắc chắn trong chuỗi. Ngủ (điều này không đảm bảo sẽ trả lại nhanh như thế nào trừ khi nó không hoạt động trước ít nhất là vào thời điểm đó). Vòng lặp rất nhanh và ngủ rất chậm, bạn không sử dụng cái sau để kiểm tra cái trước.
Ronan Thibaudau

3

Bạn thực sự có thể vặn đầu mình và thay vào đó để đóng IQueryable .foreach:

myList.ForEach(c => Console.WriteLine(c.ToString());

3
Tôi sẽ thay thế dòng mã của bạn bằng myList.ForEach(Console.WriteLine).
Mehrdad Afshari

2

Tôi sẽ không mong đợi bất cứ ai tìm thấy sự khác biệt hiệu suất "rất lớn" giữa hai người.

Tôi đoán câu trả lời phụ thuộc vào việc bộ sưu tập bạn đang cố truy cập có triển khai truy cập chỉ mục nhanh hơn hay triển khai truy cập IEnumerator nhanh hơn. Vì IEnumerator thường sử dụng bộ chỉ mục và chỉ giữ một bản sao của vị trí chỉ mục hiện tại, tôi sẽ mong đợi quyền truy cập của điều tra viên ít nhất là chậm hoặc chậm hơn so với truy cập chỉ mục trực tiếp, nhưng không nhiều.

Tất nhiên câu trả lời này không giải thích cho bất kỳ tối ưu hóa nào mà trình biên dịch có thể thực hiện.


Trình biên dịch C # thực hiện tối ưu hóa rất ít, nó thực sự để lại cho JITter.
ljs

Chà, JITter là một trình biên dịch ... Phải không?
JulianH

2

Hãy nhớ rằng vòng lặp for và vòng lặp foreach không phải lúc nào cũng tương đương. Danh sách liệt kê sẽ đưa ra một ngoại lệ nếu danh sách thay đổi, nhưng bạn sẽ không nhận được cảnh báo đó với vòng lặp for thông thường. Bạn thậm chí có thể có một ngoại lệ khác nếu danh sách thay đổi không đúng lúc.


Nếu danh sách đang thay đổi từ bên dưới bạn thì bạn không thể dựa vào điều tra viên ủng hộ một vòng lặp foreach để nói với bạn điều đó. Nó sẽ không kiểm tra lại sau khi nó trả về giá trị cho bạn, dẫn đến một cuộc đua.
hoodaticus 30/03/2017
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.