foreach 루프 내부 또는 외부에서 변수 선언 : 어느 것이 더 빠르거나 더 낫습니까?


93

이 중 어느 것이 더 빠르거나 더 좋은가요?

이 하나:

List<User> list = new List<User>();
User u;

foreach (string s in l)
{
    u = new User();
    u.Name = s;
    list.Add(u);
}

또는 이거 :

List<User> list = new List<User>();

foreach (string s in l)
{
    User u = new User();
    u.Name = s;
    list.Add(u);
}

내 초보자 개발 기술은 첫 번째 것이 더 낫다고 말하지만 내 친구는 나에게 틀렸다고 말하지만 두 번째 것이 더 좋은 이유를 알 수 없습니다.

성능에 전혀 차이가 있습니까?

답변:


113

성능면에서 두 예제는 동일한 IL로 컴파일되므로 차이가 없습니다.

두 번째는 u루프 내에서만 사용되는 경우 의도를 더 명확하게 표현하기 때문에 더 좋습니다 .


10
이 참고 변수가 람다 식 또는 익명 대리인이 캡처되는 경우 차이; 외부 가변 트랩을 참조하십시오 .
dtb

둘 다 동일한 IL로 컴파일 된 이유를 설명 할 수 있습니까? 나는 C #이 자바 스크립트처럼 함수의 맨 위에 변수 선언을 올리지 않는다고 확신합니다.
styfle 2013 년

4
@styfle 여기에 대답 귀하의 질문에.
David Sherret 2014-04-16

다음 Stack Overflow 링크는 더 자세한 답변을 제공합니다. 1) Jon Hanna2) StriplingWarrior
user3613932

14

어쨌든 가장 좋은 방법은 Name ...을받는 생성자를 사용하는 것입니다. 그렇지 않으면 중괄호 표기법을 사용합니다.

foreach (string s in l)
{
    list.Add(new User(s));
}

또는

foreach (string s in l)
{
    list.Add(new User() { Name = s });
}

또는 더 나은 LINQ :

var list = l.Select( s => new User { Name = s});

이제 첫 번째 예제는 어떤 경우에는 눈에 띄지 않을 정도로 빠르지 만 두 번째 예제는 더 읽기 쉽기 때문에 더 좋으며 컴파일러는 foreach의 범위 밖에서 사용되지 않기 때문에 변수를 버릴 수 있습니다 (모두 생략) .


6
오늘의 네크로 필적 인 코멘트 : "또는 더 나은, LINQ". 물론 그것은 한 줄의 코드이며 개발자로서 우리를 기분 좋게 만듭니다. 그러나 네 줄 버전은 훨씬 더 이해하기 쉽고 유지 관리가 가능합니다.
Oskar Austegard

5
거의. LINQ 버전을 사용하면 내가하는 일이 불변이며 모든 요소에서 작동한다는 것을 알고 있습니다.
Tordek 2013 년

6

선언으로 인해 코드가 실행되지 않으므로 성능 문제가 아닙니다.

두 번째는 의미하는 바이며 두 번째 방법으로 수행하면 어리석은 오류를 만들 가능성이 적으므로 그것을 사용하십시오. 항상 필요한 가장 작은 범위에서 변수를 선언하십시오.

게다가 더 좋은 방법은 Linq를 사용하는 것입니다.

List<User> users = l.Select(name => new User{ Name = name }).ToList();

2
저는 "항상 필요한 최소 범위에서 변수를 선언하려고 노력하십시오."라는 줄을 좋아합니다. 나는 한 줄로 질문에 아주 잘 대답 할 수 있다고 생각합니다.
Manjoor 2010

5

성능에 대한 질문이있을 때마다 측정 만하면됩니다. 테스트를 반복하고 시간을 측정하는 것입니다.

측정하지 않고 :-) 또는 생성 된 ilasm을 보지 않고 질문에 대답하려면 의미있는 반복 횟수와 코드에서 가장 비용이 많이 드는 작업에서 차이가 눈에 띄지 않을 것입니다. 몇 주문에 의한 사용자 할당 일 가능성이 있습니다 따라서 코드 명확성에 집중하고 (일반적으로해야하는 것처럼) 2로 이동합니다.

오, 늦었어요. 이런 일에 대해 걱정하지 말라고 말하려고하거나 이런 세부 사항에 얽매이지 마세요.

케이


팁을 위해 thx, 나는 내가 상처를 입었던 다른 것들도 시간을 할 것이라고 생각한다 hehe : D
Marcus

성능에 영향을 미치는 요소를 더 자세히 살펴보고 싶다면 코드 프로파일 러를 사용해보십시오. 다른 것이 없다면 어떤 유형의 코드와 작업에 가장 많은 시간이 걸리는지에 대한 아이디어를 제공하기 시작합니다. ProfileSharp 및 EqatecProfiler는 무료이며 시작하기에 충분합니다.
Kevin Shea

1

두 번째가 더 좋습니다. 당신은 각 반복에서 새로운 사용자를 갖는 것을 의미합니다.


1

기술적으로 첫 번째 예제는 새로운 변수를 할당하기 위해 스택 프레임을 이동할 필요가 없기 때문에 몇 나노초를 절약 할 수 있지만, 이것은 컴파일러가 인식하지 못하는 CPU 시간이 너무 적습니다. 차이를 최적화하십시오.


CLR이 루프를 반복 할 때마다 "새 변수"를 할당하지 않는다고 확신합니다.
dtb

컴파일러는이를 최적화 할 수 있지만 루프의 모든 변수에 대해 스택 공간을 할당해야합니다. 이것은 구현에 따라 다르며 한 구현은 단순히 스택 프레임을 동일하게 유지하는 반면 다른 구현 (예 : Mono)은 스택을 해제 한 다음 각 루프에서 다시 생성 할 수 있습니다.
Erik Funkenbusch 2009

16
메서드의 모든 지역 변수 (최상위 수준 또는 루프에 중첩 됨)는 IL의 메서드 수준 변수로 컴파일됩니다. 변수의 공간은 C #에서 선언 된 분기에 도달 할 때가 아니라 메서드가 실행되기 전에 할당됩니다.
dtb

1
@dtb이 주장에 대한 출처가 있습니까?
styfle 2014

1

이 시나리오에서는 두 번째 버전이 더 좋습니다.

일반적으로 반복 본문 내의 값에만 액세스해야하는 경우 두 번째 버전을 선택합니다. 반면에 최종 상태가있는 경우 변수는 루프 본문을 넘어서 유지하고 선언 한 다음 첫 번째 버전을 사용합니다.




0

나는이 문제를 확인하러 갔다. 놀랍게도 내 더러운 테스트에서 두 번째 옵션이 항상 약간 더 빠르다는 것을 알게되었습니다.

namespace Test
{
  class Foreach
  {
    string[] names = new[] { "ABC", "MNL", "XYZ" };

    void Method1()
    {
      List<User> list = new List<User>();
      User u;

      foreach (string s in names)
      {
        u = new User();
        u.Name = s;
        list.Add(u);
      }
    }

    void Method2()
    {

      List<User> list = new List<User>();

      foreach (string s in names)
      {
        User u = new User();
        u.Name = s;
        list.Add(u);
      }
    }
  }

  public class User { public string Name; }
}

CIL을 확인했지만 동일하지 않습니다.

여기에 이미지 설명 입력

그래서 더 나은 시험이되고 싶은 것을 준비했습니다.

namespace Test
{
  class Loop
  { 

    public TimeSpan method1 = new TimeSpan();
    public TimeSpan method2 = new TimeSpan();

    Stopwatch sw = new Stopwatch();

    public void Method1()
    {
      sw.Restart();

      C c;
      C c1;
      C c2;
      C c3;
      C c4;

      int i = 1000;
      while (i-- > 0)
      {
        c = new C();
        c1 = new C();
        c2 = new C();
        c3 = new C();
        c4 = new C();        
      }

      sw.Stop();
      method1 = method1.Add(sw.Elapsed);
    }

    public void Method2()
    {
      sw.Restart();

      int i = 1000;
      while (i-- > 0)
      {
        var c = new C();
        var c1 = new C();
        var c2 = new C();
        var c3 = new C();
        var c4 = new C();
      }

      sw.Stop();
      method2 = method2.Add(sw.Elapsed);
    }
  }

  class C { }
}

또한이 경우 두 번째 방법이 항상 승리했지만 CIL이 차이가 없음을 확인했습니다.

여기에 이미지 설명 입력

저는 CIL 읽기 전문가는 아니지만 저하 문제가 없습니다. 이미 지적했듯이 선언은 할당이 아니므로 성능에 대한 불이익이 없습니다.

테스트

namespace Test
{
  class Foreach
  {
    string[] names = new[] { "ABC", "MNL", "XYZ" };

    public TimeSpan method1 = new TimeSpan();
    public TimeSpan method2 = new TimeSpan();

    Stopwatch sw = new Stopwatch();

    void Method1()
    {
      sw.Restart();

      List<User> list = new List<User>();
      User u;

      foreach (string s in names)
      {
        u = new User();
        u.Name = s;
        list.Add(u);
      }

      sw.Stop();
      method1 = method1.Add(sw.Elapsed);
    }

    void Method2()
    {
      sw.Restart();

      List<User> list = new List<User>();

      foreach (string s in names)
      {
        User u = new User();
        u.Name = s;
        list.Add(u);
      }

      sw.Stop();
      method2 = method2.Add(sw.Elapsed);
    }
  }

  public class User { public string Name; }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.