V8에 미리 알리기: 명시적인 컴파일 힌트를 사용한 더 빠른 자바스크립트 시작
빠르게 자바스크립트를 실행하는 것은 반응형 웹 앱을 위한 핵심입니다. V8의 고급 최적화에도 불구하고, 중요한 자바스크립트를 시작 시점에 파싱하고 컴파일하는 것은 여전히 성능 병목을 초래할 수 있습니다. 초기 스크립트 컴파일 동안 어떤 자바스크립트 함수를 컴파일할지를 알면 웹 페이지 로딩 속도를 높일 수 있습니다.
네트워크에서 로드된 스크립트를 처리할 때, V8은 각 함수에 대해 즉시("적극적으로") 컴파일할지 아니면 이 과정을 지연할지를 선택해야 합니다. 컴파일되지 않은 함수가 나중에 호출되는 경우, V8은 그때야 비로소 해당 함수를 필요에 따라 컴파일해야 합니다.
자바스크립트 함수가 페이지 로드 중 호출되면, 이를 적극적으로 컴파일하면 이점이 있습니다. 그 이유는:
- 스크립트를 초기 처리할 때 최소한 가벼운 파싱을 해야만 함수의 끝을 찾을 수 있습니다. 자바스크립트에서 함수의 끝을 찾으려면 전체 구문을 파싱해야 하므로(중괄호를 세는 등의 단순한 방법은 불가능합니다), 먼저 가벼운 파싱을 하고 나중에 실제 파싱을 하면 중복 작업이 발생합니다.
- 함수의 적극적인 컴파일을 결정하면 작업이 백그라운드 스레드에서 수행되며, 이 작업의 일부는 네트워크에서 스크립트를 로드하는 동안 병렬로 처리됩니다. 반면 함수가 호출될 때만 이를 컴파일하면 병렬 작업을 수행하기엔 너무 늦어지며, 함수가 컴파일될 때까지 메인 스레드는 진행할 수 없습니다.
V8이 자바스크립트를 어떻게 파싱하고 컴파일하는지에 대한 자세한 내용은 여기에서 확인할 수 있습니다.
많은 웹 페이지가 적극적인 컴파일을 위해 적절한 함수를 선택하는 데서 이점을 얻을 수 있습니다. 예를 들어, 인기 있는 웹 페이지를 사용한 실험에서 20개 중 17개가 개선되었으며, 평균 전경 파싱 및 컴파일 시간 감소는 630ms였습니다.
우리는 웹 개발자가 어느 자바스크립트 파일과 함수가 적극적으로 컴파일될지를 제어할 수 있도록 하는 기능, 명시적인 컴파일 힌트를 개발하고 있습니다. 크롬 136은 개별 파일을 선택해 적극적으로 컴파일할 수 있는 버전을 현재 배포 중입니다.
이 버전은 특히 "핵심 파일"이 있어서 이를 적극적으로 컴파일하도록 선택하거나, 소스 파일 간에 코드를 이동시켜 이러한 핵심 파일을 만들 수 있는 경우에 유용합니다.
파일 전체에 대해 적극적인 컴파일을 트리거하려면 파일 상단에 다음과 같은 마법의 주석을 삽입하면 됩니다:
//# allFunctionsCalledOnLoad
그러나 이 기능은 신중히 사용해야 합니다 - 과도한 컴파일은 시간과 메모리를 소비할 수 있습니다!
직접 확인하기 - 컴파일 힌트의 작동 방식
V8에게 함수 이벤트를 로그하도록 알려 컴파일 힌트가 작동하는 모습을 관찰할 수 있습니다. 예를 들어, 최소한의 테스트를 설정하기 위해 다음 파일을 사용할 수 있습니다.
index.html:
<script src="script1.js"></script>
<script src="script2.js"></script>
script1.js:
function testfunc1() {
console.log('testfunc1 호출됨!');
}
testfunc1();
script2.js:
//# allFunctionsCalledOnLoad
function testfunc2() {
console.log('testfunc2 호출됨!');
}
testfunc2();
Chrome을 깨끗한 사용자 데이터 디렉토리로 실행하여 코드 캐싱이 실험 결과를 방해하지 않도록 하세요. 예시 명령줄은 다음과 같습니다:
rm -rf /tmp/chromedata && google-chrome --no-first-run --user-data-dir=/tmp/chromedata --js-flags=--log-function_events > log.txt
테스트 페이지로 이동한 후 로그에서 다음과 같은 함수 이벤트를 확인할 수 있습니다:
$ grep testfunc log.txt
function,preparse-no-resolution,5,18,60,0.036,179993,testfunc1
function,full-parse,5,18,60,0.003,181178,testfunc1
function,parse-function,5,18,60,0.014,181186,testfunc1
function,interpreter,5,18,60,0.005,181205,testfunc1
function,full-parse,6,48,90,0.005,184024,testfunc2
function,interpreter,6,48,90,0.005,184822,testfunc2
testfunc1
가 지연 컴파일되었기 때문에, 결국 호출될 때 parse-function
이벤트를 확인할 수 있습니다:
function,parse-function,5,18,60,0.014,181186,testfunc1
testfunc2
의 경우 해당 이벤트가 발생하지 않는데, 컴파일 힌트가 강제로 이를 적극적으로 파싱 및 컴파일하도록 했기 때문입니다.
명시적 컴파일 힌트의 미래
장기적으로는 개별 함수를 선택하여 적극적으로 컴파일할 수 있는 방향으로 나아가고자 합니다. 이를 통해 웹 개발자는 자신이 컴파일하고자 하는 정확한 함수를 제어할 수 있고, 컴파일 성능을 최대한 활용하여 웹 페이지를 최적화할 수 있습니다. 계속 지켜봐 주세요!