중간 언어(IR, intermediate representation)인 bytecode 형태로 변환
JIT 모드 - 생성된 bytecode를 기반으로 native code로 컴파일해서 수행
interpreter 모드 - bytecode를 하나씩 읽으며 동작
왜 JS 엔진은 JITC 방식을 쓰나
JIT는 컴파일 과정이 수행 중에 발생하기 때문에 오버헤드가 되는 단점이 있음
코드 전체를 훑어 최적화 하는 방식을 적용할 수 없고
최소한의 최적화만 적용해서 native code를 생성함
이런 단점에도 불구하고 JITC 방식을 쓰는 이유는
interpreter로 수행하는 것보다 native code의 수행 성능이 훨씬 좋기 때문
JS는 JITC보다 인터프리터가 낫다?
자바스크립트는 매우 동적인 언어
변수 타입이 수행 도중에 달라질 수 있음
class 대신 object로 상속하는 prototype-based 방식
자바스크립트 JITC는 모든 예외적인 케이스를 다 고려해 코드를 생성해야 함
→ 예외 케이스를 고려하다 보면 많은 양의 native code가 생성됨\
데이터형의 변화로 인한 예외 케이스가 발생하게 되면 slow case로 건너뜀
slow case : native code로 표현하면 양이 많아지는 동작을
native code로 뽑아내는 대신 미리 엔진 내부에 C로 구현된 함수를 호출해 동작을 수행하는 경우
이 helper 함수는 interpreter 모드로 수행할 때와 똑같은 코드를 사용하게 됨
→ JIT Compiler로 native code를 수행하는 이점이 사라지는 지점
(+ 컴파일 오버헤드를 더하면 그냥 손해보는 것..)
자바스크립트 코드는 일반적으로 자주 반복되어 수행되는 구간(hotspot) 이 적음
사용자 입력에 반응하거나 웹 페이지 레이아웃을 건드리는 일이 일반적이기 때문
= native code를 수행하는 시간에 비해 만드는(컴파일) 시간이 상대적으로 커짐
결론 : hotspot이 별로 없는 자바스크립트는 interpreter로 수행하는 것이 나음
하지만 최근에는 단순 이벤트 처리 용도 → 비즈니스 로직에 활용으로
점점 compute-intensive한 프로그램을 구현하는데 활용되고 있어 JITC를 버릴 수 없음
Adaptive JIT Compilation
위 문제를 해결하기 위해서 엔진 대부분이 adaptive compilation 방식을 채택
adaptive compilation : 모든 코드를 일괄적으로 같은 수준의 최적화를 적용하는 것이 아니라
반복 수행되는 정도에 따라 유동적으로(adaptive) 서로 다른 최적화 수준 적용
모든 코드를 기본적으로 interpreter로 수행하고,
자주 반복되는 부분(hotspot)을 발견하면 그 부분은 JITC 적용해서 native code 실행
최근에는 반복 정도에 따라서 최적화 수준이 다른 JITC 적용
사용되는 변수 타입과 값을 profile 해두었다가
Optimizing JIT(최적화 수준 더 높은)을 적용 시 이 정보를 이용해
예전 JITC에서 사용한 예외처리 루틴을 생략한 효율적 코드를 생성
여기에서 다음 반복 시 변수 유형이 바뀌어버리면
Optimizing JIT으로 생성한 코드가 더 이상 유효하지 않기 때문에
이전에 생성한 코드로 돌아가야 함 (완전 무쓸모되고 코드 생성하는데 든 비용만 날림..)
따라서 중요한 것 : 변수 타입을 함부로 막 바꾸면 안조타..
동적인 변화가 자주 일어나면 효율성이 떨어진다..
실제로 JavaScript 엔진에서 수행하는 많은 최적화들 (hidden class, inline caching 등)은 이러한 방식으로 최적화를 많이 수행하는데, 변수의 타입이나 object property가 바뀌는 등 동적인 상황이 발생할 수 있지만, 실제로는 동적인 변화가 자주 일어나지 않을 것이라고 가정합니다. 따라서 동적인 변화가 발생했을 때 페널티는 크더라도, 변화하지 않았을 때 큰 성능 이득을 볼 수 있는 최적화들을 적용합니다. 이런 대표적인 최적화로 대부분의 JavaScript JITC에서 사용하고 있는 hidden class나 inline caching을 들 수 있는데, 실제로 이러한 최적화가 엄청난 효과가 있습니다.
마음에 새기자
JavaScript 엔진을 연구하는 사람들은 그 놈의 dynamic typing 때문에 많은 고생을 합니다.
만약 성능이 좋은 JavaScript 코드를 만들고 싶다면,
JavaScript 코드를 작성할 때 마치 C나 Java처럼 static typing 언어라고 생각하세요.
특히 array가 중요한데, 하나의 array에는 하나의 type만 넣어주는 것이 최고입니다!
https://meetup.toast.com/posts/77 글 요약 정리 (2016년 작성된 글이라 이후에 어느정도 변화는 있었을 것이라 생각..)
JITC (Just-in-time Compilation)
왜 JS 엔진은 JITC 방식을 쓰나
JIT는 컴파일 과정이 수행 중에 발생하기 때문에 오버헤드가 되는 단점이 있음 코드 전체를 훑어 최적화 하는 방식을 적용할 수 없고 최소한의 최적화만 적용해서 native code를 생성함
이런 단점에도 불구하고 JITC 방식을 쓰는 이유는 interpreter로 수행하는 것보다 native code의 수행 성능이 훨씬 좋기 때문
JS는 JITC보다 인터프리터가 낫다?
자바스크립트는 매우 동적인 언어
자바스크립트 JITC는 모든 예외적인 케이스를 다 고려해 코드를 생성해야 함 → 예외 케이스를 고려하다 보면 많은 양의 native code가 생성됨\
데이터형의 변화로 인한 예외 케이스가 발생하게 되면 slow case로 건너뜀
slow case : native code로 표현하면 양이 많아지는 동작을 native code로 뽑아내는 대신 미리 엔진 내부에 C로 구현된 함수를 호출해 동작을 수행하는 경우
이 helper 함수는 interpreter 모드로 수행할 때와 똑같은 코드를 사용하게 됨 → JIT Compiler로 native code를 수행하는 이점이 사라지는 지점 (+ 컴파일 오버헤드를 더하면 그냥 손해보는 것..)
자바스크립트 코드는 일반적으로 자주 반복되어 수행되는 구간(hotspot) 이 적음 사용자 입력에 반응하거나 웹 페이지 레이아웃을 건드리는 일이 일반적이기 때문
= native code를 수행하는 시간에 비해 만드는(컴파일) 시간이 상대적으로 커짐
결론 : hotspot이 별로 없는 자바스크립트는 interpreter로 수행하는 것이 나음 하지만 최근에는 단순 이벤트 처리 용도 → 비즈니스 로직에 활용으로 점점 compute-intensive한 프로그램을 구현하는데 활용되고 있어 JITC를 버릴 수 없음
Adaptive JIT Compilation
위 문제를 해결하기 위해서 엔진 대부분이 adaptive compilation 방식을 채택
adaptive compilation : 모든 코드를 일괄적으로 같은 수준의 최적화를 적용하는 것이 아니라 반복 수행되는 정도에 따라 유동적으로(adaptive) 서로 다른 최적화 수준 적용
모든 코드를 기본적으로 interpreter로 수행하고, 자주 반복되는 부분(hotspot)을 발견하면 그 부분은 JITC 적용해서 native code 실행
최근에는 반복 정도에 따라서 최적화 수준이 다른 JITC 적용
사용되는 변수 타입과 값을 profile 해두었다가 Optimizing JIT(최적화 수준 더 높은)을 적용 시 이 정보를 이용해 예전 JITC에서 사용한 예외처리 루틴을 생략한 효율적 코드를 생성
여기에서 다음 반복 시 변수 유형이 바뀌어버리면 Optimizing JIT으로 생성한 코드가 더 이상 유효하지 않기 때문에 이전에 생성한 코드로 돌아가야 함 (완전 무쓸모되고 코드 생성하는데 든 비용만 날림..)
따라서 중요한 것 : 변수 타입을 함부로 막 바꾸면 안조타.. 동적인 변화가 자주 일어나면 효율성이 떨어진다..
마음에 새기자