루프 반전 기술이란 무엇입니까?


89

Java 용 JIT (Just -In-Time Compiler ) 최적화 기술에 대해 설명하는 문서를 살펴 보았습니다. 그중 하나는 "루프 반전"이었습니다. 그리고 문서에 따르면 :

일반 while루프를 루프로 do-while바꿉니다. 그리고 do-while루프는 if절 내에서 설정됩니다 . 이 교체는 두 번 더 적은 점프로 이어집니다.

루프 반전은 어떻게 작동하며 코드 경로를 어떻게 최적화합니까?

NB : 누군가 Java 코드의 예와 JIT가이를 네이티브 코드로 최적화하는 방법과 최신 프로세서에서 최적 인 이유를 설명 할 수 있다면 좋을 것입니다.


2
소스 코드에 대해 수행 할 작업이 아닙니다. 네이티브 코드 수준에서 발생합니다.
Marko Topolnik

2
@MarkoTopolnik 알고 있습니다. 하지만 JIT가 네이티브 코드 수준에서 어떻게이를 수행하는지 알고 싶습니다. 감사.
시도

1
오 멋지다 . en.wikipedia.org/wiki/Loop_inversion 예제가 많은 위키피디아 페이지가 있습니다. C 예제는 Java에서도 유효합니다.
Benjamin Gruenbaum

얼마 전에 질문 중 하나에서 영감을 받아이 문제에 대해 짧은 연구를 수행했습니다. 결과가 도움이 될 것입니다. stackoverflow.com/questions/16205843/java-loop-efficiency/…
Adam Siemion

이것은 점프 명령이 더 적도록 (반복 당 1 대 2) 루프 조건이 일반적으로 끝에 놓이는 위치와 동일합니까 (수행 된 점프 수가 더 적는지 여부에 관계없이)?
extremeaxe5

답변:


108
while (condition) { 
  ... 
}

워크 플로우 :

  1. 상태 확인;
  2. 거짓이면 루프 외부로 점프합니다.
  3. 한 번의 반복을 실행하십시오.
  4. 맨 위로 이동합니다.

if (condition) do {
  ...
} while (condition);

워크 플로우 :

  1. 상태 확인;
  2. 거짓이면 루프 너머로 점프합니다.
  3. 한 번의 반복을 실행하십시오.
  4. 상태 확인;
  5. 참이면 3 단계로 이동합니다.

이 두 가지를 비교하면 루프를 통해 정확히 한 단계가 있고 일반적으로 점프 수가 반복 수보다 하나 적다면 후자가 점프를 전혀 수행하지 않을 수 있음을 쉽게 알 수 있습니다. 전자는 조건을 확인하기 위해 다시 점프해야하며 조건이 거짓 일 때만 루프에서 점프해야합니다.

최신 파이프 라인 CPU 아키텍처에서 점프는 비용이 많이들 수 있습니다. CPU가 점프 전에 검사 실행을 완료하고 있으므로 해당 점프를 넘어서는 명령은 이미 파이프 라인 중간에 있습니다. 분기 예측이 실패하면이 모든 처리를 폐기해야합니다. 파이프 라인이 다시 시작되는 동안 추가 실행이 지연됩니다.

언급 된 분기 예측 설명 : 각 종류의 조건부 점프에 대해 CPU에는 결과에 대한 베팅 을 포함하는 두 개의 명령이 있습니다. 예를 들어, 마지막 반복을 제외한 모든 반복에서 점프가 이루어져야하기 때문에 루프 끝에 " 0이 아니면 점프, 0이 아닌 것에 베팅 "이라는 명령을 넣을 수 있습니다. 이렇게하면 CPU가 점프 명령 자체를 따르는 명령 대신 점프 대상을 따르는 명령으로 파이프 라인을 펌핑하기 시작합니다.

중요 사항

마십시오 하지 소스 코드 수준에서 최적화하는 방법의 예로서이 걸릴. 질문에서 이미 분명한 것처럼 첫 번째 형식에서 두 번째 형식으로의 변환은 JIT 컴파일러가 루틴의 문제로 완전히 자체적으로 수행하기 때문에 완전히 잘못된 것입니다.


51
마지막에있는 메모는 참으로 매우 매우 중요한 것입니다.
TJ Crowder

2
@AdamSiemion : 주어진 do-while소스 코드에 대해 생성 된 바이트 코드는 우리가 실제로 작성하지 않기 때문에 관련이 없습니다. while루프를 작성하고 필요한 경우 컴파일러와 JIT가 루프 반전을 통해 개선하도록 공모합니다.
TJ Crowder

1
@TJCrowder +1, 그리고 Adam에 대한 메모 : JIT 컴파일러의 최적화에 대해 생각할 때 바이트 코드를 고려 하지 마십시오 . 바이트 코드는 실행되는 실제 JIT 컴파일 코드보다 Java 소스 코드에 훨씬 더 가깝습니다. 사실, 현대 언어의 추세는 언어 지정의 일부로 바이트 코드를 전혀 갖지 않는 것입니다.
Marko Topolnik

1
중요한 정보 가 조금 더 설명 되어 추가 정보가되었을 것입니다. 왜 완전히 오도 ​​될까요?
arsaKasra

2
@arsaKasra 일반적으로 가독성과 안정성이 소스 코드의 최적화를 능가하기 때문에 잘못된 안내입니다. 특히 JIT가이 작업을 수행한다는 사실이 드러나면 (매우 미세한) 최적화를 스스로 시도해서는 안됩니다.
Radiodef dec

24

이렇게하면 항상 한 번 이상 실행되는 루프를 최적화 할 수 있습니다.

while그런 다음 일반 루프는 항상 적어도 한 번은 처음으로 돌아가고 끝에서 한 번 끝으로 점프합니다. 한 번 실행되는 간단한 루프의 예 :

int i = 0;
while (i++ < 1) {
    //do something
}  

반면에 do-while루프는 첫 번째와 마지막 점프를 건너 뜁니다. 다음은 점프없이 실행되는 위의 루프와 동일한 루프입니다.

int i = 0;
if (i++ < 1) {
    do {
        //do something
    } while (i++ < 1); 
}

+1이 정확하고 먼저 코드 예제를 추가해보세요. 같은 뭔가 boolean b = true; while(b){ b = maybeTrue();}boolean b;do{ b = maybeTrue();}while(b);충분합니다.
Benjamin Gruenbaum

걱정 마. 그것은 대답의 시작 줄을 무효화합니다. :-)
TJ Crowder

@TJ 글쎄, 입력되지 않은 루프는 여전히 최적화되지 않으며 두 경우 모두 하나의 점프가 있습니다.
Keppil

아. 넵. 죄송합니다. 한 번 이상 반복되지 않은 루프 에는 적용 할 수 없다는 의미로 읽었습니다 (그게 도움이되지 않는다는 의미가 아닙니다). 지금 당신과 함께. :-)
TJ Crowder

@Keppil 당신은 아마도 우리가 많은 수의 반복 X를 가지고있는 경우에 우리는 X 하나 사이에서 단 하나의 점프만을 저장할 것이라는 것을 분명히해야 할 것입니다.
마누엘 셀바

3

살펴 보겠습니다.

while버전 :

void foo(int n) {
    while (n < 10) {
       use(n);
       ++n;
    }
    done();
}
  1. 먼저 조건이 참이 아닌지 테스트 n하고 점프 done();합니다.
  2. 그런 다음을 사용하고 증가시킵니다 n.
  3. 이제 우리는 조건으로 돌아갑니다.
  4. 헹구고 반복하십시오.
  5. 조건이 더 이상 참이 아니면로 이동합니다 done().

do-while버전 :

(유지 보수 문제가 발생할 수있는 소스 코드에서는 실제로이 작업을 수행하지 않습니다. 컴파일러 / JIT가 대신 수행합니다.)

void foo(int n) {
    if (n < 10) {
        do {
            use(n);
            ++n;
        }
        while (n < 10);
    }
    done();
}
  1. 먼저 조건이 참이 아닌지 테스트 n하고 점프 done();합니다.
  2. 그런 다음을 사용하고 증가시킵니다 n.
  3. 이제 우리는 조건을 테스트하고 그것이 사실이라면 다시 점프합니다.
  4. 헹구고 반복하십시오.
  5. 조건이 더 이상 참이 아닐 때, 우리는 done().

예를 들어에서 n시작 9하면 do-while버전 에서 전혀 점프하지 않는 반면, while버전에서는 처음으로 돌아가서 테스트를 한 다음 그것이 사실이 아니라는 것을 알게되면 끝으로 돌아 가야합니다. .


3

루프 반전은 프로세서가 더 적은 명령으로 동일한 결과를 달성 할 수 있으므로 성능을 향상시키는 성능 최적화 기술입니다. 이것은 대부분 경계 조건에서 성능을 향상시킵니다.

이 링크 는 루프 반전에 대한 또 다른 예를 제공합니다. 감소 및 비교가 단일 명령어 세트로 구현되는 몇몇 아키텍처에서는 감소 및 비교 연산을 사용하여 for 루프를 while으로 변환하는 것이 좋습니다.

Wikipedia는 아주 좋은 예를 가지고 있으며 여기서 다시 설명하겠습니다.

 int i, a[100];
  i = 0;
  while (i < 100) {
    a[i] = 0;
    i++;
  }

컴파일러에 의해 다음으로 변환됩니다.

  int i, a[100];
  i = 0;
  if (i < 100) {
    do {
      a[i] = 0;
      i++;
    } while (i < 100);
  }

이것이 성능으로 어떻게 변환됩니까? i의 값이 99이면 프로세서는 GOTO를 수행 할 필요가 없습니다 (첫 번째 경우에 필요함). 이것은 성능을 향상시킵니다.

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