JIT 컴파일러는 일반 컴파일러와 어떻게 다릅니 까?


22

Java, Ruby 및 Python과 같은 언어의 JIT 컴파일러에 대한 과대 광고가있었습니다. JIT 컴파일러와 C / C ++ 컴파일러의 차이점은 무엇이며, Java, Ruby 또는 Python 용 컴파일러가 JIT 컴파일러라고하는 이유와 C / C ++ 컴파일러는 단지 컴파일러라고하는 이유는 무엇입니까?

답변:


17

JIT 컴파일러는 실행 직전 또는 이미 실행중인 경우에도 즉시 코드를 컴파일합니다. 이런 방식으로 코드가 실행중인 VM은 코드 실행에서 패턴을 확인하여 런타임 정보로만 가능한 최적화를 허용 할 수 있습니다. 또한 VM이 컴파일 된 버전이 어떤 이유로 든 충분하지 않다고 판단하면 (예 : 너무 많은 캐시 누락 또는 특정 예외를 자주 발생시키는 코드) 훨씬 다른 방법으로 다시 컴파일하기로 결정할 수 있습니다. 편집.

반면에 C 및 C ++ 컴파일러는 전통적으로 JIT가 아닙니다. 개발자 컴퓨터에서 한 번만 단일 샷으로 컴파일 한 다음 실행 파일이 생성됩니다.


캐시 미스를 추적하고 그에 따라 컴파일 전략을 조정하는 JIT 컴파일러가 있습니까? @ 빅터
마틴 버거

@MartinBerger 사용 가능한 JIT 컴파일러가 그렇게하는지 모르겠지만 왜 그렇지 않습니까? 컴퓨터가 더 강력하고 컴퓨터 과학이 발전함에 따라 사람들은 프로그램에 더 많은 최적화를 적용 할 수 있습니다. 예를 들어, 자바가 태어 났을 때 컴파일 된 것보다 20 배나 느리지 만, 지금은 성능이 크게 저하되지 않고 일부 컴파일러와 비교 될 수 있습니다
phuclv

오래 전에 블로그 게시물에서이 소식을 들었습니다. 컴파일러는 현재 CPU에 따라 다르게 컴파일 될 수 있습니다. 예를 들어 CPU가 SSE / AVX를 지원하는 것을 감지하면 일부 코드를 자동으로 벡터화 할 수 있습니다. 최신 SIMD 확장을 사용할 수 있으면 새로운 확장으로 컴파일 될 수 있으므로 프로그래머는 아무것도 변경할 필요가 없습니다. 또는 더 큰 캐시를 활용하거나 캐시 미스를 줄이기 위해 작업 / 데이터를 정렬 할 수있는 경우
phuclv

@ LyuuVĩnhPhúc Chào! 나는 기뻐하지만, 차이점은 예를 들어 CPU 유형이나 캐시 크기는 계산 전체에서 변하지 않는 정적이므로 정적 컴파일러로도 쉽게 수행 할 수 있다는 것입니다. 캐시 미스 OTOH는 매우 역동적입니다.
Martin Berger

12

JIT는 JIT (Just-In-Time) 컴파일러의 약자이며 이름은 잘못되었습니다. 런타임 중에는 가치있는 코드 최적화를 결정하고 적용합니다. 일반적인 컴파일러를 대체하지는 않지만 인터프리터의 일부입니다. 중간 코드를 사용하는 Java 같은 언어는이 점에 유의 모두 : 중간 코드 번역 소스에 대한 일반 컴파일러, 성능 부스트에 대한 인터프리터에 포함 된 JIT.

코드 최적화는 확실히 "클래식"컴파일러에 의해 수행 될 수 있지만 주요 차이점은 JIT 컴파일러가 런타임시 데이터 에 액세스 할 수 있다는 것 입니다. 이것은 장점입니다. 제대로 활용하는 것은 어려울 수 있습니다.

예를 들어 다음과 같은 코드를 고려하십시오.

m(a : String, b : String, k : Int) {
  val c : Int;
  switch (k) {
    case 0 : { c = 7; break; }
    ...
    case 17 : { c = complicatedMethod(k, a+b); break; }
  }

  return a.length + b.length - c + 2*k;
}

정상적인 컴파일러는 이것에 대해 너무 많은 것을 할 수 없습니다. 그러나 JIT 컴파일러 mk==0어떤 이유로 만 호출되는 것을 감지 할 수 있습니다 (시간이 지남에 따라 코드가 변경 될 수 있음). 그런 다음 더 작은 버전의 코드를 생성 할 수 있습니다 (그리고 개념적으로 마이너 포인트라고 생각하더라도 네이티브 코드로 컴파일 할 수 있습니다).

m(a : String, b : String) {
  return a.length + b.length - 7;
}

이 시점에서 아마도 메소드 호출이 사소한 것처럼 인라인 될 것입니다.

분명히 Sun은 javacJava 6에서 사용되는 대부분의 최적화를 무시했습니다 . 이러한 최적화로 인해 JIT가 많은 작업을 수행하는 것이 어려워지고 순진하게 컴파일 된 코드가 결국 더 빨리 실행되었다고 들었습니다. 그림을 이동.


그건 그렇고, 유형 삭제 (예 : Java의 제네릭)가있는 경우 JIT는 실제로 wrt 유형에 불리합니다.
Raphael

따라서 런타임 환경은 최적화하기 위해 데이터를 수집해야하기 때문에 컴파일 된 언어보다 런타임이 더 복잡합니다.
saadtaame

2
많은 경우에, JIT 대체 할 수 없을 것입니다 m확인하지 않았다 버전과 k는 것을 증명 할 수없는 것이기 때문에 m것이 결코 아닌 0으로 호출 할 수 k있지만, 심지어 대체 할 수 있음을 입증 할 수없이 그것은 static miss_count; if (k==0) return a.length+b.length-7; else if (miss_count++ < 16) { ... unoptimized code for m 과 함께 ...} else { ... consider other optimizations...}.
supercat

1
@supercat에 동의하고 귀하의 예가 실제로 오해의 소지가 있다고 생각합니다. 입력 또는 환경의 기능이 아니라는 것을 의미하는 k=0 always 인 경우에만 테스트를 중단하는 것이 안전합니다. 그러나이를 결정하는 데는 정적 분석이 필요합니다. 이는 매우 비싸고 컴파일 시간에 저렴한 비용입니다. 코드 블록을 통한 하나의 경로가 다른 경로보다 훨씬 더 자주 사용되면 JIT가 이길 수 있으며이 경로에 특화된 코드 버전이 훨씬 빠릅니다. 그러나 JIT는 여전히 빠른 경로가 적용되는지 여부를 확인 하고 그렇지 않은 경우 "느린 경로"를 테스트합니다 .
j_random_hacker

예 : 함수는 Base* p매개 변수를 가져 와서 이를 통해 가상 함수를 호출합니다. 런타임 분석에 따르면 항상 (또는 거의 항상) 가리키는 실제 객체는 Derived1유형 인 것으로 보입니다 . JIT는 정적으로 해결 된 (또는 심지어 인라인 된) Derived1메소드 호출로 함수의 새 버전을 생성 할 수 있습니다 . 이 코드 앞에는 pvtable 포인터가 예상 Derived1테이블 을 가리키는 지 여부를 확인하는 조건부가 있습니다 . 그렇지 않으면 대신 동적으로 해결되는 느린 메소드 호출로 함수의 원래 버전으로 건너 뜁니다.
j_random_hacker
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.