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


312

루프 내부에서 반복적으로 반복되는 것과 달리 루프 전에 버리기 변수를 선언하면 (성능) 차이가 있는지 항상 궁금합니다. 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 사례에 관심이 있습니다.


이것은 Android 플랫폼 용 Java 코드를 작성할 때 중요합니다. Google은 시간이 중요한 코드가 for 루프 외부에서 증가하는 변수를 for 루프 내부에서 선언하는 것처럼 해당 환경에서 매번 다시 선언하는 것이 좋습니다. 값 비싼 알고리즘의 경우 성능 차이가 두드러집니다.
AaronCarson

1
@AaronCarson 당신은 구글이 제안에 대한 링크를 제공하시기 바랍니다 수
비탈리 Zinchenko에게

답변:


256

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

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

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


5
Double 대신 String을 다루는 경우 여전히 "b"가 더 낫습니까?
Antoops

3
@Antoops-예, b는 선언되는 변수의 데이터 유형과 관련이없는 이유로 더 좋습니다. 문자열과 다른 이유는 무엇입니까?
Daniel Earwicker

215

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

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

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

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


12
당신은 내가 프로파일 링에 대한 결과를 게시하려고했는데 나를 이겼습니다.
Mark Davidson

14
변수가 루프에 대해 로컬 인 경우 반복 할 때마다 보존 할 필요가 없으므로 레지스터에 머무를 수 있습니다.

142
OP가 스스로 구성 할 수있는 의견 / 이론 만이 아니라 실제로 테스트 한 결과 +1입니다 .
MGOwen

3
@GoodPerson 정직하게, 나는 그렇게하고 싶습니다. 나는 거의 동일한 코드 조각으로 통계를 실행하기를 원하는 50,000,000-100,000,000 반복에 대해 내 컴퓨터에서 약 10 번이 테스트를 실행했습니다. 답은 거의 900ms (50M 이상 반복)의 여백으로 거의 똑같이 나뉘어져 있습니다. 나의 첫번째 생각은 그것이 "잡음"이 될 것이라는 점이지만, 조금씩 기울어 질 수도 있습니다. 이 노력은 나에게 순수하게 학문적 인 것처럼 보입니다 (대부분의 실제 응용 프로그램의 경우). 어쨌든 결과를보고 싶습니다.) 누구나 동의합니까?
javatarz

3
설정을 문서화하지 않고 테스트 결과를 보여주는 것은 가치가 없습니다. 두 코드 조각 모두 동일한 바이트 코드를 생성하는이 경우 특히 그렇습니다. 따라서 측정 된 차이는 테스트 조건이 충분하지 않음을 나타냅니다.
Holger

66

언어와 정확한 사용법에 따라 다릅니다. 예를 들어 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변수가 있다는 것입니다.


3
예제 B (원래 질문)에서 실제로 매번 새 변수를 작성합니까? 스택의 눈에 무슨 일이?
Royi Namir

@Jon, C # 1.0의 버그입니까? 이상적 Outer으로 9 해서는 안 됩니까?
nawfal

@ nawfal : 나는 당신이 무슨 뜻인지 모르겠습니다. 람다 식은 1.0에 없었으며 외부 9입니다. 무슨 버그입니까?
Jon Skeet

@ nawfal : 내 요점은 C # 1.0에 언어 내부 기능이 없기 때문에 루프 내부에서 변수 선언과 외부 선언 사이의 차이점을 알 수 있습니다 (둘 다 컴파일되었다고 가정). C # 2.0에서 변경되었습니다. 버그가 없습니다.
Jon Skeet

@ JonSkeet 오 예, 지금 당신을 얻습니다 .1.0에서와 같은 변수를 닫을 수 없다는 사실을 완전히 간과했습니다. 내 나쁜! :)
nawfal

35

다음은 .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. 둘 다 성능에 큰 차이가 있습니다. 컴파일러가 더 큰 범위의 의도를 파악할 수 없기 때문에 컴파일러가 코드를 최적화 할 수없는 경우가 많이 있습니다. 그러나 기본 사항을 대부분 최적화 할 수 있습니다.


이런 식으로 int j = 0에 대해 (; j <0x3e8; j ++) 한 번만 변수에 대해 선언하고 사이클에 대해서는 각각 선언하지 않습니다. 2) 과제는 다른 모든 옵션보다 더 뚱뚱합니다. 3) 따라서 모범 사례 규칙은 반복 외부의 선언입니다.
luka

24

이것은 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...

1
나는 수년 동안 VB.NET을 사용해 왔으며 이것을 보지 못했습니다 !!
ChrisA

12
예, 실제로 이것을 알아내는 것은 불쾌합니다.
Michael Haren

다음은 Paul Vick의 이에 대한 참조입니다. panopticoncentral.net/archive/2006/03/28/11552.aspx
ferventcoder

1
@eschneider @ferventcoder 불행히도 @PaulV는 그의 오래된 블로그 게시물삭제 하기로 결정 했기 때문에 이제는 죽은 링크입니다.
Mark Hurd

예, 최근에이 문제가 발생했습니다. 이 ...에 대한 몇 가지 공식 문서를 찾고 있었다
에릭 슈나이더

15

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

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 결과입니다. 두 코드가 동일한 것을 생성한다는 증거가 아닙니까?


3
그래, 그리고 당신이 이것을 한 것이 멋지지만, 이것은 사람들이 언어 / 컴파일러 의존성에 대해 말한 것으로 되돌아갑니다. JIT 또는 해석 된 언어 성능이 어떻게 영향을 받는지 궁금합니다.
137717

12

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


예, 그러나 그것은별로 중요하지 않습니다. 1 억 번 실행되는 for 루프로 간단한 테스트를 실행했으며 루프 외부 선언에 유리한 가장 큰 차이는 8ms라는 것을 알았습니다. 일반적으로 3-4와 비슷하고 때로는 루프 외부에서 선언하여 WORSE (최대 4ms)를 선언했지만 일반적이지 않았습니다.
137717

11

컴파일러를 사용하지 않고 항상 A를 사용하고 다음과 같이 다시 쓸 수도 있습니다.

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

이것은 여전히 intermediateResult루프 범위로 제한 되지만 각 반복 중에 다시 선언되지는 않습니다.


12
개념적으로 변수가 반복마다 별도로 반복되는 대신 루프 지속 시간 동안 유지되도록 하시겠습니까? 나는 거의하지 않습니다. 달리해야 할 아주 좋은 이유가 없다면 가능한 한 명확하게 자신의 의도를 나타내는 코드를 작성하십시오.
Jon Skeet

4
아, 좋은 타협, 나는 이것을 결코 생각하지 않았다! IMO, 코드는 시각적으로 조금 덜 명확 해집니다)
Rabarberski

2
@ Jon-나는 OP가 실제로 중간 값으로 무엇을하고 있는지 전혀 모른다. 방금 고려해야 할 옵션이라고 생각했습니다.
Triptych 2012 년

6

제 생각에는 b가 더 나은 구조입니다. a에서, 중간 결과의 마지막 값은 루프가 완료된 후에 고정됩니다.

편집 : 값 유형과 큰 차이는 없지만 참조 유형은 다소 무거울 수 있습니다. 개인적으로 변수 정리가 가능한 빨리 역 참조되는 것을 좋아합니다.


sticks around after your loop is finished-이것은 파이썬과 같은 언어에서는 중요하지 않지만 함수가 끝날 때까지 바인딩 된 이름이 붙어 있습니다.
new123456

@ new123456 : OP가 질문 일반적으로 질문 한 경우에도 Java 관련 사항 요청했습니다. 많은 C 파생 언어에는 C, C ++, Perl (my 키워드 포함), C # 및 Java 가 있습니다.
Powerlord

나는 알고있다-그것은 비판이 아니라 관찰이었다.
새로운 123456

5

필자는 소수의 컴파일러가 둘 다 동일한 코드로 최적화 할 수 있다고 생각하지만 모두는 아닙니다. 그래서 나는 당신이 전자와 더 낫다고 말하고 싶습니다. 후자의 유일한 이유는 선언 된 변수가 루프 내 에서만 사용되도록하기 위해서입니다 .


5

일반적으로 가능한 가장 안쪽 범위에서 변수를 선언합니다. 따라서 루프 외부에서 중간 결과를 사용하지 않으면 B를 사용합니다.


5

동료는 첫 번째 양식을 선호하며 최적화를 말하고 선언을 재사용하는 것을 선호합니다.

나는 두 번째 것을 선호합니다 (그리고 동료를 설득하려고 노력하십시오! ;-)).

  • 변수의 범위를 필요한 곳으로 줄입니다. 이는 좋은 일입니다.
  • Java는 성능에 큰 차이가 없도록 충분히 최적화합니다. IIRC, 아마도 두 번째 형태가 더 빠릅니다.

어쨌든 컴파일러 및 / 또는 JVM의 품질에 의존하는 조기 최적화 범주에 속합니다.


5

람다 등에서 변수를 사용하는 경우 C #에는 차이가 있습니다. 그러나 일반적으로 컴파일러는 변수가 루프 내에서만 사용된다고 가정하고 기본적으로 동일한 작업을 수행합니다.

그것들이 기본적으로 동일하다는 것을 감안할 때 : 버전 b는 변수가 루프 후에 사용될 수 없으며, 사용될 수 없다는 것을 독자에게 훨씬 더 분명하게 만듭니다. 또한 버전 b는 훨씬 쉽게 리팩토링됩니다. 버전 a에서 루프 본문을 자체 메소드로 추출하는 것이 더 어렵습니다.또한 버전 b는 그러한 리팩토링에 대한 부작용이 없음을 보증합니다.

따라서 버전 a는 아무런 이점이 없으므로 코드에 대한 추론을 어렵게 만듭니다.


5

글쎄, 당신은 항상 그에 대한 범위를 만들 수 있습니다 :

{ //Or if(true) if the language doesn't support making scopes like this
    double intermediateResult;
    for (int i=0; i<1000; i++) {
        intermediateResult = i;
        System.out.println(intermediateResult);
    }
}

이 방법으로 변수를 한 번만 선언하면 루프를 떠날 때 죽습니다.


4

나는 항상 루프 안에서 변수를 선언하면 메모리를 낭비한다고 생각했습니다. 이와 같은 것이 있다면 :

for(;;) {
  Object o = new Object();
}

그런 다음 각 반복에 대해 객체를 생성해야 할뿐만 아니라 각 객체에 대해 새로운 참조가 할당되어야합니다. 가비지 수집기가 느린 경우 정리해야 할 매달려있는 참조가 많이있는 것 같습니다.

그러나 이것이 있다면 :

Object o;
for(;;) {
  o = new Object();
}

그런 다음 단일 참조 만 작성하고 매번 새 오브젝트를 지정합니다. 물론, 범위를 벗어나는 데 시간이 조금 더 걸릴 수 있지만 다루는 참조는 하나뿐입니다.


3
참조가 'for'루프 내에서 선언 되더라도 각 객체에 대해 새로운 참조가 할당되지 않습니다. 두 경우 모두 : 1) 'o'는 로컬 변수이며 함수 시작시 스택 공간이 한 번 할당됩니다. 2) 각 반복마다 새로운 객체가 생성됩니다. 따라서 성능에는 차이가 없습니다. 코드 구성, 가독성 및 유지 관리 성을 위해 루프 내에서 참조를 선언하는 것이 좋습니다.
Ajoy Bhatia

1
Java를 말할 수는 없지만 .NET에서는 첫 번째 예제에서 각 객체에 대한 참조가 '할당'되지 않습니다. 해당 로컬 (메소드에 대한) 변수의 스택에 단일 항목이 있습니다. 예를 들어 생성 된 IL은 동일합니다.
Jesse C. Slicer

3

컴파일러에 달려 있고 일반적인 대답을하기가 어렵다고 생각합니다.


3

내 연습은 다음과 같습니다.

  • 변수 유형이 간단한 경우 (int, double, ...) 변형 b (내부)를 선호합니다 .
    이유 : 변수의 범위를 줄입니다.

  • 변수 유형이 단순하지 않은 경우 (일부 종류 class또는 struct) 변형 a (외부)를 선호합니다 .
    이유 : ctor-dtor 통화 수를 줄입니다.


1

성능 관점에서 볼 때 외부가 훨씬 좋습니다.

public static void outside() {
    double intermediateResult;
    for(int i=0; i < Integer.MAX_VALUE; i++){
        intermediateResult = i;
    }
}

public static void inside() {
    for(int i=0; i < Integer.MAX_VALUE; i++){
        double intermediateResult = i;
    }
}

나는 두 가지 기능을 각각 10 억 번 실행했습니다. outside ()는 65 밀리 초가 걸렸습니다. inside ()는 1.5 초가 걸렸습니다.


2
그렇다면 최적화되지 않은 컴파일 디버그 여야합니까?
Tomasz Przychodzki

이런 식으로 int j = 0에 대해 (; j <0x3e8; j ++) 한 번만 변수에 대해 선언하고 사이클에 대해서는 각각 선언하지 않습니다. 2) 과제는 다른 모든 옵션보다 더 뚱뚱합니다. 3) 따라서 모범 사례 규칙은 반복 외부의 선언입니다.
luka

1

관심이 있다면 Node 4.0.0으로 JS를 테스트했습니다. 루프 외부에서 선언하면 시험 당 1 억 회 반복하여 평균 1000 회 이상의 시험에서 ~ 0.5ms의 성능이 향상되었습니다. 계속해서 B라고하는 가장 읽기 쉽고 관리하기 쉬운 방식으로 작성하겠습니다. 코드를 바이올린에 넣었지만 성능이 우수한 Node 모듈을 사용했습니다. 코드는 다음과 같습니다.

var now = require("../node_modules/performance-now")

// declare vars inside loop
function varInside(){
    for(var i = 0; i < 100000000; i++){
        var temp = i;
        var temp2 = i + 1;
        var temp3 = i + 2;
    }
}

// declare vars outside loop
function varOutside(){
    var temp;
    var temp2;
    var temp3;
    for(var i = 0; i < 100000000; i++){
        temp = i
        temp2 = i + 1
        temp3 = i + 2
    }
}

// for computing average execution times
var insideAvg = 0;
var outsideAvg = 0;

// run varInside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varInside()
    var end = now()
    insideAvg = (insideAvg + (end-start)) / 2
}

// run varOutside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varOutside()
    var end = now()
    outsideAvg = (outsideAvg + (end-start)) / 2
}

console.log('declared inside loop', insideAvg)
console.log('declared outside loop', outsideAvg)

0

A) B)보다 안전한 내기입니다. ......... 'int'또는 'float'보다는 루프에서 구조를 초기화하는 경우 어떻게 생각하십니까?

처럼

typedef struct loop_example{

JXTZ hi; // where JXTZ could be another type...say closed source lib 
         // you include in Makefile

}loop_example_struct;

//then....

int j = 0; // declare here or face c99 error if in loop - depends on compiler setting

for ( ;j++; )
{
   loop_example loop_object; // guess the result in memory heap?
}

당신은 확실히 메모리 누수 문제에 직면 할 것입니다!. 따라서 'A'는 더 안전한 내기이고 'B'는 가까운 소스 라이브러리를 사용하는 메모리 누적 esp에 취약합니다. 특히 Linux의 하위 도구 'Helgrind'에서 'Valgrind'Tool을 사용하여 확인할 수 있습니다.


0

흥미로운 질문입니다. 내 경험상 코드에 대해이 문제를 토론 할 때 고려해야 할 궁극적 인 질문이 있습니다.

변수가 전역이어야하는 이유가 있습니까?

코드를 구성하는 것이 좋으며 코드 줄이 더 적기 때문에 로컬에서 여러 번이 아닌 전역에서 변수를 한 번만 선언하는 것이 좋습니다. 그러나 하나의 메소드 내에서만 로컬로 선언 해야하는 경우 해당 메소드에서 초기화하므로 변수가 해당 메소드와 독점적으로 관련이 있음이 분명합니다. 후자의 옵션을 선택할 경우 초기화되는 메소드 외부에서이 변수를 호출하지 않도록주의하십시오.

또한 참고로, 목적이 거의 동일하더라도 서로 다른 메소드간에 로컬 변수 이름을 복제하지 마십시오. 혼란스러워집니다.


1
lol 나는 여러 가지 이유로 동의하지 않습니다 ... 그러나, 투표권이 없습니다 ... 나는 당신의 선택권을 존중합니다
Grantly

0

이것은 더 나은 형태입니다

double intermediateResult;
int i = byte.MinValue;

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

1) 이런 식으로 한 번에 두 변수가 아닌 사이클마다 선언되지 않습니다. 2) 과제는 다른 모든 옵션보다 더 뚱뚱합니다. 3) 따라서 모범 사례 규칙은 반복 외부의 선언입니다.


0

Go에서 같은 것을 시도하고 go tool compile -Sgo 1.9.4를 사용하여 컴파일러 출력을 비교했습니다.

어셈블러 출력에 따른 차이가 없습니다.


0

나는이 같은 질문을 오랫동안했다. 그래서 더 간단한 코드를 테스트했습니다.

결론 : 들어 이러한 경우NO의 성능 차이.

외부 루프 케이스

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

내부 루프 케이스

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

IntelliJ의 디 컴파일러에서 컴파일 된 파일을 확인했으며 두 경우 모두 동일합니다. Test.class

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

또한이 답변에 제공된 방법을 사용하여 두 경우 모두에 대한 코드를 분해했습니다 . 답과 관련된 부분 만 보여 드리겠습니다

외부 루프 케이스

Code:
  stack=2, locals=3, args_size=1
     0: iconst_0
     1: istore_2
     2: iload_2
     3: sipush        1000
     6: if_icmpge     26
     9: iload_2
    10: iconst_2
    11: iadd
    12: istore_1
    13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    16: iload_1
    17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
    20: iinc          2, 1
    23: goto          2
    26: return
LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13      13     1 intermediateResult   I
            2      24     2     i   I
            0      27     0  args   [Ljava/lang/String;

내부 루프 케이스

Code:
      stack=2, locals=3, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: sipush        1000
         6: if_icmpge     26
         9: iload_1
        10: iconst_2
        11: iadd
        12: istore_2
        13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: iload_2
        17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        20: iinc          1, 1
        23: goto          2
        26: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13       7     2 intermediateResult   I
            2      24     1     i   I
            0      27     0  args   [Ljava/lang/String;

당신이주의를 지불하는 경우에만 Slot할당 iintermediateResultLocalVariableTable출현 순서의 제품으로 교체된다. 슬롯의 동일한 차이가 다른 코드 줄에 반영됩니다.

  • 추가 작업이 수행되지 않습니다
  • intermediateResult 두 경우 모두 여전히 지역 변수이므로 액세스 시간에 차이가 없습니다.

보너스

컴파일러는 수많은 최적화 작업을 수행하며이 경우 어떤 일이 발생하는지 살펴보십시오.

제로 작업 사례

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

제로 작업 디 컴파일

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

-1

컴파일러가 충분히 똑똑하다는 것을 알고 있더라도 그것에 의존하고 싶지 않으며 a) 변형을 사용합니다.

b) 변형은 루프 본문 후에 중간 결과를 사용할 수 없게 만드는 경우에만 의미가 있습니다. 하지만 어쨌든 그런 절박한 상황을 상상할 수 없습니다 ....

편집 : Jon Skeet 은 루프 내에서 변수 선언이 실제로 의미 론적 차이를 만들 수 있음을 보여 주면서 매우 좋은 지적을했습니다.

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