Programing

.NET에서 'for'또는 'foreach'중 어느 루프가 더 빠르게 실행됩니까?

lottogame 2020. 3. 8. 09:52
반응형

.NET에서 'for'또는 'foreach'중 어느 루프가 더 빠르게 실행됩니까?


C # / VB.NET / .NET에서 어떤 루프는 빠르게 실행, for또는 foreach?

오래 전에 루프 forforeach루프 보다 빠르게 작동 한다는 것을 읽은 이래로 모든 컬렉션, 일반 컬렉션, 모든 배열 등에 적용되는 것으로 가정했습니다.

Google을 sc이 뒤져서 몇 개의 기사를 찾았지만 대부분 결론을 내리지 못하고 (기사에 대한 의견을 읽음) 공개되지 않았습니다.

이상적인 것은 각 시나리오를 나열하고 최상의 솔루션을 제시하는 것입니다.

예를 들어 (어떻게 해야하는지에 대한 예) :

  1. 1000 개 이상의 문자열 배열을 반복하는 for경우foreach
  2. IList(일반적이지 않은) 문자열 을 반복하는 foreach것보다 낫습니다.for

웹에서 몇 가지 참고 자료를 찾았습니다.

  1. Emmanuel Schanzer의 원본 그랜드 낡은 기사
  2. CodeProject FOREACH 대. 에 대한
  3. 로 - 블로그 foreach에 아닙니다 또는 foreach질문입니다,
  4. ASP.NET 포럼-NET 1.1 C # forvsforeach

[편집하다]

그 가독성 측면 외에도 사실과 수치에 관심이 있습니다. 마지막 마일 성능 최적화가 중요한 응용 프로그램이 있습니다.


Patrick Smacchia 지난 달에 대해 다음과 같은 결론을 내렸다.

  • List의 for 루프는 List의 foreach 루프보다 2 배 이상 저렴합니다.
  • 어레이의 루핑은 List의 루핑보다 약 2 배 저렴합니다.
  • 결과적으로 for를 사용하여 배열을 반복하는 것은 foreach를 사용하여 List를 반복하는 것보다 5 배 저렴합니다 (내 생각에 우리 모두가하는 일입니다).

먼저 Dmitry의 답변에 대한 반소 . 배열의 경우 C # 컴파일러 foreach는 동등한 for루프 와 거의 동일한 코드를 생성 합니다. 이 벤치 마크의 결과가 기본적으로 동일한 이유를 설명합니다.

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);
    }
}

결과 :

For loop: 16638
Foreach loop: 16529

다음으로 컬렉션 유형에 대한 Greg의 요점에 대한 유효성 검사- List<double>의 배열을 a 변경 하면 근본적으로 다른 결과를 얻습니다. 일반적으로 속도가 상당히 느릴뿐만 아니라 foreach로 액세스하는 것보다 foreach가 상당히 느려집니다. 그럼에도 불구하고, 나는 항상 코드를 단순하게 만드는 for 루프보다 foreach를 선호합니다. 가독성은 거의 항상 중요하지만 마이크로 최적화는 거의 없기 때문입니다.


foreach루프는보다 더 구체적 의도를 보여 for루프를 .

foreach루프를 사용하면 코드를 사용하는 모든 사용자에게 컬렉션의 위치에 관계없이 컬렉션의 각 멤버에게 무언가를 수행 할 계획임을 알 수 있습니다. 또한 원본 컬렉션을 수정하지 않고 있음을 나타내며 시도하면 예외가 발생합니다.

의 또 다른 장점 foreach은 어떤에서 작동한다는 것입니다 IEnumerable같은 경우, for단지가에 대한 의미한다 IList각 요소가 실제로 인덱스를 보유하고 있습니다.

그러나 요소의 인덱스를 사용해야하는 경우에는 물론 for루프 를 사용해야합니다 . 그러나 인덱스를 사용할 필요가 없다면 코드를 복잡하게 만드는 것입니다.

내가 아는 한 성능에 큰 영향은 없습니다. 미래의 어떤 단계에서는 foreach여러 코어에서 실행 하기 위해 코드를 사용 하는 것이 더 쉬울 수 있지만 지금은 걱정할 것이 없습니다.


성능에 대한 논쟁이있을 때마다, 당신은 당신의 사례를 지원하기 위해 양적 결과를 사용할 수 있도록 작은 테스트를 작성하면됩니다.

StopWatch 클래스를 사용하고 정확성을 위해 수백만 번 반복하십시오. (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

손가락은이 쇼의 결과를 넘어서 그 차이는 무시할 만하다는 것을 보여 주며 가장 유지 보수가 쉬운 코드로 결과를 얻을 수 있습니다.


항상 가깝습니다. 배열의 경우 때로는 for 약간 빠르지 만 foreach표현력이 뛰어나고 LINQ 등을 제공합니다. 일반적으로으로 고정하십시오 foreach.

또한 foreach일부 시나리오에서 최적화 될 수 있습니다. 예를 들어, 링크 된 목록은 인덱서에 의해 끔찍할 수 있지만으로 빠를 수 있습니다 foreach. 실제로이 표준 LinkedList<T>은 이러한 이유로 인덱서를 제공하지도 않습니다.


내 생각에 99 %의 경우에는 그다지 중요하지 않을 것입니다. 왜 가장 이해하기 쉬운 것보다 가장 빠른 것 대신 더 빠른 것을 선택하겠습니까?


둘 사이에 큰 성능 차이는 없을 것입니다. 항상 그렇듯이 "더 빠를까?" 질문, 당신은 항상 "나는 이것을 측정 할 수 있습니다"라고 생각해야합니다.

루프 본문에서 동일한 작업을 수행하는 두 개의 루프를 작성하고 둘 다 실행 및 시간을 정하고 속도의 차이가 무엇인지 확인하십시오. 거의 빈 바디와 실제로 수행 할 작업과 유사한 루프 바디로이 작업을 수행하십시오. 컬렉션 유형마다 성능 특성이 다를 수 있으므로 사용중인 컬렉션 유형으로 시도하십시오.


루프보다 루프 선호하는 데는 매우 좋은 이유가 있습니다. 루프 를 사용할 수 있다면 상사가 옳 아야합니다.foreachforforeach

그러나 모든 반복이 단순히 순서대로 목록을 통과하는 것은 아닙니다. 그가 금지 하고 있다면 , 그렇습니다.

내가 당신이라면, 내가 할 일은 당신의 모든 자연 for 루프를 재귀로 바꾸는 것 입니다. 그것은 그를 가르 칠 것이며, 그것은 또한 당신에게 좋은 정신 운동입니다.


TechEd 2005의 Jeffrey Richter :

"수년에 걸쳐 C # 컴파일러는 기본적으로 거짓말 쟁이입니다." .. "많은 것들에 관한 것입니다." .. "foreach 루프를 수행 할 때처럼 ...".. "... 작은 한 줄의 코드이지만, C # 컴파일러가 놀라운 결과를 내기 위해 뱉어내는 것입니다. try / finally block, finally 블록 내부에서 변수를 IDisposable 인터페이스로 캐스팅하고 캐스트가 성공하면 Dispose 메서드를 호출하고 루프 내에서 Current 속성과 MoveNext 메서드를 반복하여 루프 내에서 반복합니다. 많은 사람들이 코딩하기 매우 쉽고 수행하기 쉽기 때문에 foreach를 사용하고 있습니다. ".."foreach는 성능 측면에서 매우 좋지 않습니다.

주문형 웹 캐스트 : http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US


이건 말도 안돼 for 루프, 성능 측면 또는 기타를 금지해야 할 강력한 이유는 없습니다.

성능 벤치 마크 및 기타 인수에 대해서는 Jon Skeet의 블로그참조하십시오 .


객체 컬렉션으로 작업하는 foreach것이 좋지만 숫자를 늘리면 for루프가 좋습니다.

마지막 경우 다음과 같은 작업을 수행 할 수 있습니다.

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

그러나 확실히 더 잘 수행되지는 않습니다. 실제로에 비해 성능이 떨어집니다 for.


이것은 당신을 저장해야합니다 :

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

사용하다:

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

더 큰 승리를 위해 세 명의 대의원을 매개 변수로 사용할 수 있습니다.


for-,- foreach루프 에서 속도의 차이는 배열, 목록 등과 같은 일반적인 구조를 반복 할 때 작고 LINQ컬렉션에 대한 쿼리를 수행하는 것이 거의 느리지 만 작성하는 것이 더 좋습니다! 다른 포스터가 말했듯이 밀리 초의 추가 성능보다는 표현력을 얻으십시오.

지금까지 말하지 않은 것은 foreach루프가 컴파일 될 때 반복되는 컬렉션을 기반으로 컴파일러에 의해 최적화된다는 것입니다. 즉, 어떤 루프를 사용해야하는지 확실하지 않으면 루프를 사용해야합니다. foreach루프가 컴파일 될 때 가장 적합한 루프를 생성합니다. 더 읽기 쉽습니다.

foreach루프의 또 다른 주요 장점 은 컬렉션 구현이 int array에서 List<int>for로 foreach변경되면 루프에서 코드를 변경할 필요가 없다는 것입니다.

foreach (int i in myCollection)

위의 내용은 컬렉션의 유형에 관계없이 동일하지만 for루프 myCollection에서 a array에서 a로 변경하면 다음이 빌드되지 않습니다 List.

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

"for 루프를 사용할 수 있는지 확인하는 데 사용할 수있는 인수가 있습니까?"

아니, 당신의 상사가 어떤 프로그래밍 언어 구성을 사용하는지 말해 줄 수있는 수준까지 미시 관리하고 있다면, 실제로 말할 수있는 것은 없습니다. 죄송합니다.


아마도 열거하고있는 컬렉션 유형과 인덱서 구현에 따라 다릅니다. 그러나 일반적으로 사용 foreach하는 것이 더 나은 방법 일 수 있습니다.

또한 IEnumerable인덱서가 아닌 모든 항목에서 작동합니다 .


여기에는 가장 "빠른"질문과 같은 두 가지 대답이 있습니다.

1) 측정하지 않으면 모릅니다.

2) (왜냐하면 ...) 상황에 따라 다릅니다.

반복 할 IEnumerable의 유형 (또는 유형)에 대해 "this [int index]"메소드의 가격과 비교하여 "MoveNext ()"메소드의 가격에 따라 다릅니다.

"foreach"키워드는 일련의 작업에 대한 약어입니다. IEnumerable에서 GetEnumerator ()를 한 번 호출하고, 반복마다 한 번 MoveNext ()를 호출하고, 유형 검사 등을 수행합니다. 성능 측정에 가장 영향을 줄 수있는 것은 O (N) 번 호출되기 때문에 MoveNext () 비용입니다. 아마도 싸지 만 아닐 수도 있습니다.

"for"키워드는 더 예측 가능해 보이지만 대부분의 "for"루프 안에서 "collection [index]"와 같은 것을 찾을 수 있습니다. 이것은 간단한 배열 인덱싱 작업처럼 보이지만 실제로는 메서드 호출이며 비용은 전적으로 반복하는 컬렉션의 특성에 달려 있습니다. 아마 싸지 만 아닐 수도 있습니다.

컬렉션의 기본 구조가 본질적으로 연결된 목록 인 경우 MoveNext는 비싸지 만 인덱서에는 O (N) 비용이있을 수 있으므로 "for"루프 O (N * N)의 실제 비용이 발생합니다.


모든 언어 구성에는 사용하기에 적절한 시간과 장소가 있습니다. C # 언어에 네 개의 개별 반복문 이있는 이유 가 있습니다. 각 반복문 은 특정 목적을 위해 있으며 적절한 용도로 사용됩니다.

나는 상사와 함께 앉아 왜 for루프에 목적이 있는지 합리적으로 설명하려고합니다 . 경우가 있습니다 for반복 블록이 더 명확보다 알고리즘을 설명 foreach반복합니다. 이것이 사실이면 그것들을 사용하는 것이 적절합니다.

나는 또한 당신의 상사에게 지적 할 것입니다-성능은 실제적인 방법으로 문제가되지 않아야하며 어떤 식으로도 문제가되어서는 안됩니다. 알고리즘을 간결하고 의미 있고 유지 보수가 가능한 방식으로 표현하는 것이 더 중요합니다. 이와 같은 마이크로 최적화는 성능 최적화의 요점을 완전히 벗어납니다. 왜냐하면 실제 성능상의 이점은 루프 구조 조정이 아니라 알고리즘 재 설계 및 리팩토링에서 비롯되기 때문입니다.

합리적인 토론 후에도 여전히 권위 주의적 견해가 있다면, 어떻게 진행할 것인지는 귀하에게 달려 있습니다. 개인적으로 저는 합리적인 사고가 권장되지 않는 환경에서 일하는 것을 좋아하지 않으며 다른 고용주 아래 다른 위치로 이사하는 것을 고려할 것입니다. 그러나 화를 내기 전에 토론을 강력히 권장합니다. 간단한 오해가있을 수 있습니다.


실제 루핑 구조가 아니라 루프 내부 에서 수행 하는 것이 성능에 영향을 미칩니다 (사례가 중요하지 않다고 가정).


요점 for보다 더 빠른지 아닌지 foreach. 하나를 선택하면 성능에 큰 영향을 줄 것입니다.

응용 프로그램을 최적화하는 가장 좋은 방법은 실제 코드를 프로파일 링하는 것입니다. 그것은 가장 많은 일과 시간을 설명하는 방법을 정확하게 보여줄 것입니다. 먼저 최적화하십시오. 여전히 성능이 만족스럽지 않으면 절차를 반복하십시오.

일반적으로 마이크로 최적화는 별다른 이점이 없기 때문에 마이크로 최적화를 피하는 것이 좋습니다. 식별 된 핫 경로를 최적화 할 때만 예외입니다 (예 : 프로파일 링에서 자주 사용되는 몇 가지 방법을 식별하는 경우 이러한 방법을 광범위하게 최적화하는 것이 좋습니다).


Deep .NET 에서 읽을 수 있습니다 -1 부 반복

.NET 소스 코드에서 디스 어셈블리까지의 결과 (첫 번째 초기화없이)를 다룹니다.

예를 들어-foreach 루프를 사용한 배열 반복 : 여기에 이미지 설명을 입력하십시오

그리고-foreach 루프로 반복 목록을 작성하십시오. 여기에 이미지 설명을 입력하십시오

그리고 최종 결과 : 여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오


둘은 거의 똑같은 방식으로 실행됩니다. 둘 다 사용하는 코드를 작성한 다음 IL을 보여주십시오. 성능에 차이가 없음을 의미하는 비슷한 계산을 보여 주어야합니다.


for 구현하는 데 더 간단한 로직이 있으므로 foreach보다 빠릅니다.


특정 속도 최적화 프로세스를 수행하지 않는 한 코드를 읽고 유지 관리하기 가장 쉬운 방법을 사용한다고 말합니다.

컬렉션 클래스 중 하나와 같이 반복자가 이미 설정되어 있으면 foreach가 좋은 옵션입니다. 그리고 반복하는 정수 범위라면 아마도 더 깨끗할 것입니다.


Jeffrey Richter는 최근 팟 캐스트에서 for와 foreach의 성능 차이에 대해 이야기했습니다. http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317


대부분의 경우 실제로 차이가 없습니다.

일반적으로 명시적인 숫자 인덱스가없는 경우 항상 foreach를 사용해야하며 실제로 반복 가능한 컬렉션이없는 경우에는 항상 사용해야합니다 (예 : 위 삼각형의 2 차원 배열 그리드에서 반복) . 선택의 여지가있는 경우가 있습니다.

마법의 숫자가 코드에 나타나기 시작하면 for 루프를 유지하는 것이 조금 더 어려울 수 있다고 주장 할 수 있습니다. for 루프를 사용할 수 없어 짜증이 나고 for 루프가 금지되어 있기 때문에 대신 컬렉션을 빌드하거나 람다를 사용하여 하위 컬렉션을 빌드해야합니다.


실제로 그의 머리를 조이고 대신 IQueryable .foreach 클로저를 찾으십시오.

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


foreach루프가 List 더 빨리 반복되는 것을 발견했습니다 . 아래의 테스트 결과를 참조하십시오. 아래 코드 에서 시간을 측정하기 위해 루프를 array사용하여 별도로 크기가 100, 10000 및 100000 인 반복합니다.forforeach

여기에 이미지 설명을 입력하십시오

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);
    }

업데이트

@jgauffin 제안 후 @johnskeet 코드를 사용하고 for루프 array가 다음보다 빠릅니다.

  • 배열이있는 Foreach 루프.
  • 목록이있는 루프.
  • 목록이있는 Foreach 루프.

아래의 테스트 결과와 코드를 참조하십시오.

여기에 이미지 설명을 입력하십시오

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);
    }

나는 그 둘 사이에서 "거대한"성능 차이를 발견 할 사람이 없을 것입니다.

I guess the answer depends on the whether the collection you are trying to access has a faster indexer access implementation or a faster IEnumerator access implementation. Since IEnumerator often uses the indexer and just holds a copy of the current index position, I would expect enumerator access to be at least as slow or slower than direct index access, but not by much.

Of course this answer doesn't account for any optimizations the compiler may implement.


Keep in mind that the for-loop and foreach-loop are not always equivalent. List enumerators will throw an exception if the list changes, but you won't always get that warning with a normal for loop. You might even get a different exception if the list changes at just the wrong time.


It seems a bit strange to totally forbid the use of something like a for loop.

두 루프 사이의 많은 성능 차이를 다루는 흥미로운 기사가 있습니다 .

나는 개인적으로 foreach를 for 루프보다 조금 더 읽기 쉽다고 말하지만, 현재 작업에 가장 잘 사용해야하며 for 루프가 더 적합한 경우 foreach 루프를 포함하기 위해 긴 코드를 작성할 필요가 없습니다.

참고 URL : https://stackoverflow.com/questions/365615/in-net-which-loop-runs-faster-for-or-foreach



반응형