programing

루프 내부 또는 외부 변수 선언

lovecodes 2022. 7. 28. 23:46
반응형

루프 내부 또는 외부 변수 선언

왜 다음과 같이 하면 되는 것입니까?

String str;
while (condition) {
    str = calculateStr();
    .....
}

그러나 이것은 위험/잘못된 것으로 알려져 있습니다.

while (condition) {
    String str = calculateStr();
    .....
}

루프 외부에 변수를 선언할 필요가 있습니까?

로컬 변수의 범위는 항상 가능한 한 작게 해야 합니다.

에서는, 「 」라고 것을 상정하고 있습니다.str외부에서는 사용되지 않습니다.while입니다. 루프 루프가 '루프'를 '루프', '루프'를 '루프' 안에 선언하기 입니다.왜냐하면, 이 루프가 그 루프를 선언되어 있기 때문입니다.whilecompileloop이 때문에 .

그래서...str루프 외부에서는 사용되지 않습니다.이것은, 가능한 최소의 범위입니다.str이 while 루프 내에 있습니다.

그래서 그 대답은 단호하게str확실히 그 사이에 선언해야 한다.도, 반대도 반대도 .'만도 、 '도 、 족 ' 、 만도 、 만도 도도도도

이 규칙을 위반할 가능성이 있는 유일한 경우는 어떤 이유로든 모든 클럭사이클을 코드에서 추출해야 하는 중요한 경우입니다.이 경우 내부 스코프의 모든 반복에 대해 재인스턴스화하지 않고 외부 스코프에서 어떤 것을 인스턴스화하여 재사용하는 것을 검토할 수 있습니다.단, Java 문자열의 불변성으로 인해 이 예에는 적용되지 않습니다.str의 새로운 인스턴스는 항상 루프의 선두에 생성되며 str의 마지막에 폐기되어야 하므로 이 예에서는 최적화할 수 없습니다.

편집: (아래 답변에 내 코멘트를 삽입)

어떤 경우에도 올바른 방법은 모든 코드를 올바르게 작성하고 제품의 성능 요건을 확립하고 최종 제품을 이 요건에 대해 측정하여 충족시키지 못할 경우 최적화하는 것입니다.그리고 보통 몇 군데에서 적절한 알고리즘 최적화를 할 수 있습니다.이것에 의해, 프로그램의 퍼포먼스 요건을 만족시킬 수 있습니다.이것에 의해서, 고객의 코드 베이스 전체를 조사해, 클럭 사이클을 짜내기 위해서, 수정이나 해킹을 실시할 필요는 없습니다.

이 두 가지(유사) 예제의 바이트 코드를 비교했습니다.

1을 살펴보겠습니다. 예:

package inside;

public class Test {
    public static void main(String[] args) {
        while(true){
            String str = String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

후에javac Test.java,javap -c Test을 사용하다

public class inside.Test extends java.lang.Object{
public inside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

2에 대해 살펴보겠습니다. :

package outside;

public class Test {
    public static void main(String[] args) {
        String str;
        while(true){
            str =  String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

후에javac Test.java,javap -c Test을 사용하다

public class outside.Test extends java.lang.Object{
public outside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

관찰 결과 이 두 가지 예에서는 차이가 없습니다.JVM 사양의 결과입니다.

단, 베스트 코딩 프랙티스의 이름으로 변수를 가능한 한 작은 범위에서 선언하는 것이 좋습니다(이 예에서는 변수가 사용되는 곳은 루프 내부뿐이므로).

최소 범위에서 개체를 선언하면 읽기 쉬움이 향상됩니다.

현재의 컴파일러에서는 퍼포먼스는 중요하지 않습니다.(이 시나리오에서는)
유지보수의 관점에서 보면 두 번째 옵션이 더 좋습니다.
변수를 가능한 한 좁은 범위에서 같은 장소에서 선언 및 초기화합니다.

Donald Ervin Knuth가 말한 대로:

97% 정도의 작은 효율은 잊어야 합니다.최적화는 모든 악의 근원이라고 할 수 있습니다.

즉, 프로그래머가 성능 고려 사항을 코드 설계에 영향을 미치는 상황입니다.최적화로 인해 코드가 복잡해지고 프로그래머가 최적화로 인해 주의가 산만해지기 때문에 설계가 원래보다 깨끗하지 않거나 코드가 잘못될 수 있습니다.

str루프"; "루프"; "루프"; "루프"; "루프 ""; "루프"; "루프"; "루프"; "루프"; ""★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

업데이트된 답변으로 건너뛰십시오.

퍼포먼스를 중시하는 유저에게는, 시스템을 제외해 주세요.루프를 1바이트로 제한합니다.Windows 7 Professional 64비트 및 JDK-1.7.0_21에서는 더블(테스트 1/2)과 스트링(3/4)을 사용하는 경우의 경과시간을 밀리초 단위로 나타냅니다.바이트 코드(아래 test1과 test2에 대해서도 지정)는 동일하지 않습니다.가변적이고 비교적 복잡한 오브젝트로 테스트하는 것은 귀찮았습니다.

더블

테스트 1: 2710 밀리초

테스트 2: 2790밀리초

String(테스트에서는 double을 문자열로 바꿉니다)

테스트 3 소요 시간: 1200 밀리초

테스트 4: 3000밀리초

바이트 코드 컴파일 및 가져오기

javac.exe LocalTest1.java

javap.exe -c LocalTest1 > LocalTest1.bc


public class LocalTest1 {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        double test;
        for (double i = 0; i < 1000000000; i++) {
            test = i;
        }
        long finish = System.currentTimeMillis();
        System.out.println("Test1 Took: " + (finish - start) + " msecs");
    }

}

public class LocalTest2 {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        for (double i = 0; i < 1000000000; i++) {
            double test = i;
        }
        long finish = System.currentTimeMillis();
        System.out.println("Test1 Took: " + (finish - start) + " msecs");
    }
}


Compiled from "LocalTest1.java"
public class LocalTest1 {
  public LocalTest1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: lstore_1
       4: dconst_0
       5: dstore        5
       7: dload         5
       9: ldc2_w        #3                  // double 1.0E9d
      12: dcmpg
      13: ifge          28
      16: dload         5
      18: dstore_3
      19: dload         5
      21: dconst_1
      22: dadd
      23: dstore        5
      25: goto          7
      28: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
      31: lstore        5
      33: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      36: new           #6                  // class java/lang/StringBuilder
      39: dup
      40: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      43: ldc           #8                  // String Test1 Took:
      45: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      48: lload         5
      50: lload_1
      51: lsub
      52: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
      55: ldc           #11                 // String  msecs
      57: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      60: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      63: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      66: return
}


Compiled from "LocalTest2.java"
public class LocalTest2 {
  public LocalTest2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: lstore_1
       4: dconst_0
       5: dstore_3
       6: dload_3
       7: ldc2_w        #3                  // double 1.0E9d
      10: dcmpg
      11: ifge          24
      14: dload_3
      15: dstore        5
      17: dload_3
      18: dconst_1
      19: dadd
      20: dstore_3
      21: goto          6
      24: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
      27: lstore_3
      28: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: new           #6                  // class java/lang/StringBuilder
      34: dup
      35: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
      38: ldc           #8                  // String Test1 Took:
      40: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      43: lload_3
      44: lload_1
      45: lsub
      46: invokevirtual #10                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
      49: ldc           #11                 // String  msecs
      51: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      54: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      57: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      60: return
}

갱신된 답변

성능을 모든 JVM 최적화와 비교하는 것은 정말 쉽지 않습니다.하지만 어느 정도 가능합니다.Google 캘리퍼에서 더 나은 테스트 및 세부 결과 제공

  1. 블로그 상세 정보:루프 내부에서 변수를 선언해야 합니까, 아니면 루프 전에 선언해야 합니까?
  2. GitHub 저장소: https://github.com/gunduru/jvdt
  3. 이중 케이스와 1억 루프의 테스트 결과(모든 JVM 상세도 포함) :https://microbenchmarks.appspot.com/runs/b1cef8d1-0e2c-4120-be61-a99faff625b4

1,759.209 이전에 선언된 내부 2,242.308

  • 1,759.209 ns 이전 선언
  • 선언된 내부 2,242.308 ns

이중 선언을 위한 부분 테스트 코드

이것은 위의 코드와 동일하지 않습니다.코드화만 하면 더미루프가 건너뛰기 때문에 적어도 무언가를 할당하고 반환해야 합니다.이는 캘리퍼 설명서에서도 권장됩니다.

@Param int size; // Set automatically by framework, provided in the Main
/**
* Variable is declared inside the loop.
*
* @param reps
* @return
*/
public double timeDeclaredInside(int reps) {
    /* Dummy variable needed to workaround smart JVM */
    double dummy = 0;

    /* Test loop */
    for (double i = 0; i <= size; i++) {

        /* Declaration and assignment */
        double test = i;

        /* Dummy assignment to fake JVM */
        if(i == size) {
            dummy = test;
        }
    }
    return dummy;
}

/**
* Variable is declared before the loop.
*
* @param reps
* @return
*/
public double timeDeclaredBefore(int reps) {

    /* Dummy variable needed to workaround smart JVM */
    double dummy = 0;

    /* Actual test variable */
    double test = 0;

    /* Test loop */
    for (double i = 0; i <= size; i++) {

        /* Assignment */
        test = i;

        /* Not actually needed here, but we need consistent performance results */
        if(i == size) {
            dummy = test;
        }
    }
    return dummy;
}

개요: declared Before는 퍼포먼스가 향상되었음을 나타냅니다(매우 작음).최소 범위의 원칙에 반합니다.JVM이 실제로 이 작업을 수행합니다.

내부에서는 변수를 더 적게 볼수록 더 잘 볼 수 있습니다.

이 문제의 해결 방법 중 하나는 while 루프를 캡슐화하는 가변 스코프를 제공하는 것입니다.

{
  // all tmp loop variables here ....
  // ....
  String str;
  while(condition){
      str = calculateStr();
      .....
  }
}

외부 스코프가 종료되면 자동으로 참조 해제됩니다.

를 사용할 필요가 없는 경우strwhile loop(슬롯 관련) 후 두 번째 조건, 즉

  while(condition){
        String str = calculateStr();
        .....
    }

스택에 오브젝트를 정의하는 것이 좋습니다.condition정말이에요., 필요한 경우 사용합니다.

당신의 질문에 답할 수 있는 가장 좋은 자료는 다음 게시물이라고 생각합니다.

변수를 선언하기 전 또는 루프 상태에서의 차이점

내가 이해한 바로는 이것은 언어에 의존할 것이다.IIRC Java는 이를 최적화하기 때문에 차이가 없지만 JavaScript(예를 들어)는 루프에서 매번 전체 메모리를 할당합니다.특히 자바에서는 프로파일링을 완료하면 두 번째가 더 빨리 실행된다고 생각합니다.

변수는 가능한 한 사용되는 위치에 가깝게 선언해야 합니다.

이를 통해 RAII(Resource Acquisition Is Initialization)가 쉬워집니다.

변수의 범위를 좁혀줍니다.이것에 의해, 옵티마이저는 보다 효율적으로 동작할 수 있습니다.

Google Android Development Guide에 따르면 가변 범위는 제한되어야 합니다.다음 링크를 확인하십시오.

변수 범위 제한

문자열 선언str을 벗어나서whileloop을 사용하여 내부 및 외부에서 참조할 수 있습니다.whileloop. 문자열을 선언합니다.str내부whileloop을 사용하면, 그 에서만 참조할 수 있습니다.while고리.

많은 사람들이 지적했듯이

String str;
while(condition){
    str = calculateStr();
    .....
}

이것보다 나은 것은 없습니다.

while(condition){
    String str = calculateStr();
    .....
}

따라서 재사용하지 않을 경우 변수를 범위 밖에 선언하지 마십시오.

루프 내부에서 선언하면 각 변수의 범위가 제한됩니다.이 모든 것은 변수의 범위에 대한 프로젝트의 요구 사항에 따라 달라집니다.

실제로 위의 질문은 프로그래밍 문제입니다.코드를 어떻게 프로그래밍하시겠습니까?'STR'에 액세스하려면 어디에 액세스해야 합니까?글로벌 변수로 로컬로 사용되는 변수를 선언할 필요는 없습니다.프로그래밍의 기본이라고 생각합니다.

str변수는 사용 가능하며 코드 아래에서 실행되는 동안에도 메모리의 일부 공간을 예약합니다.

 String str;
    while(condition){
        str = calculateStr();
        .....
    }

str할 수 됩니다.는, 「사용할 수 없습니다」에 되어 있습니다.str변수(아래 코드).

while(condition){
    String str = calculateStr();
    .....
}

두 번째 순서로 하면 시스템 메모리가 감소하고 퍼포먼스가 향상됩니다.

아, 네, 네, 네. 첫 에서는 ", " " 를 할 수 .str변수 while loop 밖에 있습니다.두 번째는 그렇지 않습니다.

물건의 크기도 중요하다고 생각합니다.제 프로젝트 중 하나에서는 대규모 2차원 어레이를 선언하고 초기화했기 때문에 애플리케이션이 메모리 부족 예외를 발생시켰습니다.대신 선언을 루프 밖으로 이동하고 반복 시작 시 어레이를 클리어했습니다.

이 질문의 거의 모든 사용자에게 경고:다음은 Java 7을 사용하는 컴퓨터에서 루프 내에서는 200배 더 느릴 수 있는 샘플 코드입니다(메모리 소비량도 약간 다릅니다).그러나 그것은 범위뿐만 아니라 할당에 관한 것이다.

public class Test
{
    private final static int STUFF_SIZE = 512;
    private final static long LOOP = 10000000l;

    private static class Foo
    {
        private long[] bigStuff = new long[STUFF_SIZE];

        public Foo(long value)
        {
            setValue(value);
        }

        public void setValue(long value)
        {
            // Putting value in a random place.
            bigStuff[(int) (value % STUFF_SIZE)] = value;
        }

        public long getValue()
        {
            // Retrieving whatever value.
            return bigStuff[STUFF_SIZE / 2];
        }
    }

    public static long test1()
    {
        long total = 0;

        for (long i = 0; i < LOOP; i++)
        {
            Foo foo = new Foo(i);
            total += foo.getValue();
        }

        return total;
    }

    public static long test2()
    {
        long total = 0;

        Foo foo = new Foo(0);
        for (long i = 0; i < LOOP; i++)
        {
            foo.setValue(i);
            total += foo.getValue();
        }

        return total;
    }

    public static void main(String[] args)
    {
        long start;

        start = System.currentTimeMillis();
        test1();
        System.out.println(System.currentTimeMillis() - start);

        start = System.currentTimeMillis();
        test2();
        System.out.println(System.currentTimeMillis() - start);
    }
}

결론:로컬 변수의 크기에 따라 큰 변수가 아니더라도 차이가 클 수 있습니다.

루프 외부 또는 내부가 문제가 될 수 있습니다.

.NullPointerException calculateStr()method가 null을 반환하고 str에서 메서드를 호출하려고 합니다.

일반적으로 값이 null인 변수는 사용하지 마십시오.그나저나 클래스 속성으로 보면 더 강해요.

언급URL : https://stackoverflow.com/questions/8803674/declaring-variables-inside-or-outside-of-a-loop

반응형