[java] 루프 전 또는 루프에서 변수 선언의 차이점은 무엇입니까?

루프 내부에서 반복적으로 반복되는 것과 달리 루프 전에 버리기 변수를 선언하면 (성능) 차이가 있는지 항상 궁금합니다. Java 의 (무의미한) 예 :

a) 루프 전 선언 :

double intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

b) 루프 내에서 (반복적으로 ) 선언 :

for(int i=0; i < 1000; i++){
    double intermediateResult = i;
    System.out.println(intermediateResult);
}

어느 것이 더 낫 습니까 , a 또는 b ?

반복되는 변수 선언 (예 : b )이 이론상 더 많은 오버 헤드 생성 한다고 생각하지만 컴파일러는 중요하기 때문에 충분히 똑똑합니다. 예제 b 는 더 컴팩트하고 변수의 범위를 사용되는 곳으로 제한한다는 이점이 있습니다. 여전히 예제 a 에 따라 코딩하는 경향이 있습니다 .

편집 : 특히 Java 사례에 관심이 있습니다.



답변

a 또는 b 중 어느 것이 더 낫 습니까?

성능 관점에서 측정해야합니다. (그리고 내 의견으로는, 차이를 측정 할 수 있다면 컴파일러는 좋지 않습니다).

유지 관리 측면에서 b 가 더 좋습니다. 가장 좁은 범위에서 같은 장소에서 변수를 선언하고 초기화하십시오. 선언과 초기화 사이에 간격을 두지 말고 필요하지 않은 네임 스페이스를 오염시키지 마십시오.


답변

글쎄, A 및 B 예제를 각각 20 번 실행하여 1 억 회 반복했습니다. (JVM-1.5.0)

A : 평균 실행 시간 : .074 초

B : 평균 실행 시간 : .067 초

놀랍게도 B는 약간 빨랐습니다. 정확하게 측정 할 수 있다면 컴퓨터가 말을하기가 쉽지 않습니다. 나는 그것을 A 방식으로 코딩 할 것이지만 실제로는 중요하지 않다고 말할 것입니다.


답변

언어와 정확한 사용법에 따라 다릅니다. 예를 들어 C # 1에서는 아무런 차이가 없었습니다. C # 2에서 로컬 변수가 익명 메소드 (또는 C # 3의 람다 식)에 의해 캡처되면 매우 큰 차이를 만들 수 있습니다.

예:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        int outer;
        for (int i=0; i < 10; i++)
        {
            outer = i;
            int inner = i;
            actions.Add(() => Console.WriteLine("Inner={0}, Outer={1}", inner, outer));
        }

        foreach (Action action in actions)
        {
            action();
        }
    }
}

산출:

Inner=0, Outer=9
Inner=1, Outer=9
Inner=2, Outer=9
Inner=3, Outer=9
Inner=4, Outer=9
Inner=5, Outer=9
Inner=6, Outer=9
Inner=7, Outer=9
Inner=8, Outer=9
Inner=9, Outer=9

차이점은 모든 조치가 동일한 outer변수를 캡처 하지만 각각 고유 한 별도의 inner변수가 있다는 것입니다.


답변

다음은 .NET에서 작성하고 컴파일 한 것입니다.

double r0;
for (int i = 0; i < 1000; i++) {
    r0 = i*i;
    Console.WriteLine(r0);
}

for (int j = 0; j < 1000; j++) {
    double r1 = j*j;
    Console.WriteLine(r1);
}

이것이 CIL 이 코드로 다시 렌더링 될 때 .NET Reflector 에서 얻은 것 입니다.

for (int i = 0; i < 0x3e8; i++)
{
    double r0 = i * i;
    Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
    double r1 = j * j;
    Console.WriteLine(r1);
}

따라서 컴파일 후 둘 다 똑같이 보입니다. 관리되는 언어에서 코드는 CL / 바이트 코드로 변환되고 실행시 기계 언어로 변환됩니다. 따라서 기계 언어에서 이중은 스택에서 생성되지 않을 수도 있습니다. 코드가 WriteLine함수 의 임시 변수임을 반영하는 레지스터 일 수 있습니다 . 루프에 대해서만 전체 세트 최적화 규칙이 있습니다. 따라서 보통 사람들은 특히 관리되는 언어에서 걱정하지 않아도됩니다. 관리 코드를 최적화 할 수있는 경우가 있습니다. 예를 들어 다음을 사용하여 많은 수의 문자열을 연결해야하는 경우string a; a+=anotherstring[i] vs 대를 사용StringBuilder. 둘 다 성능에 큰 차이가 있습니다. 컴파일러가 더 큰 범위의 의도를 파악할 수 없기 때문에 컴파일러가 코드를 최적화 할 수없는 경우가 많이 있습니다. 그러나 기본 사항을 대부분 최적화 할 수 있습니다.


답변

이것은 VB.NET의 문제입니다. 이 예제에서는 Visual Basic 결과가 변수를 다시 초기화하지 않습니다.

For i as Integer = 1 to 100
    Dim j as Integer
    Console.WriteLine(j)
    j = i
Next

' Output: 0 1 2 3 4...

이렇게하면 처음에 0이 인쇄되고 (Visual Basic 변수에는 선언시 기본값이 설정됩니다!) 그 i이후 에는 매번 출력됩니다.

그러나를 추가하면 = 0기대할 수있는 것을 얻습니다.

For i as Integer = 1 to 100
    Dim j as Integer = 0
    Console.WriteLine(j)
    j = i
Next

'Output: 0 0 0 0 0...


답변

나는 간단한 테스트를했다 :

int b;
for (int i = 0; i < 10; i++) {
    b = i;
}

vs

for (int i = 0; i < 10; i++) {
    int b = i;
}

이 코드를 gcc-5.2.0으로 컴파일했습니다. 그런 다음이 두 코드의 기본 ()을 분해하면 결과입니다.

1º :

   0x00000000004004b6 <+0>:     push   rbp
   0x00000000004004b7 <+1>:     mov    rbp,rsp
   0x00000000004004ba <+4>:     mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret

vs

   0x00000000004004b6 <+0>: push   rbp
   0x00000000004004b7 <+1>: mov    rbp,rsp
   0x00000000004004ba <+4>: mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret 

어느 asa 결과와 같은 asm 결과입니다. 두 코드가 동일한 것을 생성한다는 증거가 아닙니까?


답변

언어에 따라 다릅니다. IIRC C #은이를 최적화하므로 차이는 없지만 JavaScript (예 : JavaScript)는 매번 전체 메모리 할당을 수행합니다.