눈에 보이는 자바스크립트 엔진 동작원리

원문 (영어): https://dev.to/lydiahallie/javascript-visualized-the-javascript-engine-4cdf
원저자: Lydia Hallie

This post is a Korean translation of “JavaScript Visualized: the JavaScript Engine“. Thanks, Lydia for the great article and a generous permission for translations.

자바스크립트는 정말 짱입니다(어그로 아닙니다). 그런데 여러분이 작성한 코드를 컴퓨터가 어떻게 이해하는 것일까요? 저같은 자바스크립트 개발자들은 컴파일러를 다룰 일이 거의 없습니다. 그래도 자바스크립트 엔진이 인간친화적인 자바스크립트 코드를 처리하여 기계가 이해할 수 있도록 만드는 기본적인 동작 원리에 대해서 알아두는게 좋습니다.

노트: 이 글은 Node.js에서 사용되는 V8 엔진과 크로미움 기반 웹브라우저를 기준으로 작성하였습니다.

HTML 파서는 HTML 소스에서 <script> 태그를 만나게 됩니다. 네트웍이나 캐시 또는 이미 설치된 서비스 워커(service worker)로부터 코드를 가져옵니다. 가져온 코드는 바이트 스트림이며 바이트 스트림 디코더(byte stream decoder)가 코드를 다운로드를 받으면서 동시에 디코딩을 진행합니다.

바이트 스트림 디코더는 바이트 스트림으로부터 토큰을 만듭니다. 예를 들어, 0066f, 0075u, 006en, 0063c, 0074t, 0069i, 006fo, 006en, 그리고 공백 문자열이 나오면 function이라는 단어가 되겠네요. 참고로, function은 자바스크립트의 예약어입니다. 이때 하나의 토큰이 만들어지며 파서로 보내집니다(정확하게는 전처리 파서인데 위의 그림에는 보이지 않습니다. 나중에 설명하겠습니다.) 이런식으로 나머지 바이트 스트림도 처리합니다.

자바스크립트 엔진은 전처리 파서와 파서 두개의 파서를 사용합니다. 전처리 파서는 입력된 토큰이 구문 에러가 있는지 검토합니다. 이렇게 함으로써 파서가 코드를 처리하다가 에러를 만나게될 경우를 줄일 수 있습니다.

에러가 없다면 파서는 바이트 스트림 디코더에서 입력받은 토큰을 가지고 노드를 만듭니다. 이 노드를 가지고 AST (Abstract Syntax Tree 또는 추상 구문 트리)를 구성합니다. 🌳

다음은 인터프리터의 차례입니다. 인터프리터는 AST(추상 구문 트리)를 따라가면서 바이트 코드를 생성합니다. 바이트 코드가 완전히 생성되면 AST를 삭제하여 메모리를 청소합니다. 이제야 기계가 직접 다룰 수 있는 뭔가가 나왔습니다. 🎉

바이트 코드 자체로도 충분히 빠르다고 할 수 있지만 더 빠르게 만들 필요가 있습니다. 바이트 코드를 실행하면서 부가적인 정보를 수집합니다. 어떤 로직이 반복적으로 실행되는지 어떤 데이터가 자주 사용되는지에 대한 정보입니다. 타입 피드백(Type Feedback)이라고 부르죠. 예를 들어, 어떤 함수가 수십번 실행된다면 그 함수를 최적화함으로써 속도를 향상시킬 수 있습니다! 🏃🏽‍♀️

타입 피드백은 바이트 코드와 함께 최적화 컴파일러(Optimizing Compiler)에게 보내집니다. 최적화 컴파일러는 들어온 입력을 바탕으로 아주 최적화된 기계 코드를 작성합니다. 🚀

자바스크립트는 동적 타입 언어입니다. 데이터의 타입이 계속해서 바뀐다는 의미죠. 그런데 자바스크립트 엔진이 어떤 변수가 품은 데이터의 타입을 매번 검토해야 한다면 매우 비효율적이겠죠.

그래서 인라인 캐싱(Inline Caching)이라는 기법을 사용합니다. 코드를 메모리에 캐시하여 여러번 실행되는 동작에 대해서는 동일한 값을 바로 제공하는 것이죠. 이를테면 어떤 함수가 100번 실행이 되었고 지금까지 항상 같은 결과값을 만들어냈다면 101번째에도 같은 결과를 만들거라고 짐작할 수 있습니다.

자, 다음은 sum이라는 함수입니다. 지금까지 항상 숫자 값 두개가 인자로 전달되었다고 가정합시다.

위의 예에서는 숫자 3을 리턴하겠네요. 그리고 다음번 실행시에도 두개의 숫자 값이 전달될 것이라고 가정할 겁니다.

가정이 참인 경우, 동적 검토가 필요하지 않고 이미 알고있는 특정 메모리 영역에서 결과를 바로 읽을 수가 있습니다. 가정이 거짓이라면, 최적화된 코드를 버리고 원래의 바이트 코드로 돌아가게 됩니다.

예를 들어, 같은 함수를 다시 실행하여 숫자 대신 문자열을 넘기는 경우를 가정해 봅시다. 자바스크립트는 동적 타입 언어라서 이것이 문법적으로 문제가 없습니다!

위의 예에서 sum이라는 함수는 2라는 숫자는 형변환이 되면서 ‘1’이라는 문자열에 붙습니다. 그리고 “12”(숫자가 아니고 문자열입니다)라는 결과를 만듭니다. 이 경우 최적화전의 바이트 코드를 실행하고 타입 피드백의 정보를 갱신합니다.

이 포스트가 여러분에게 도움이 되었으면 좋겠습니다! 😊자바스크립트 힙, 콜스택 등 이번 포스트에서 다루지 않은 부분이 정말 많은데요, 다음에 다룰 수 있으면 좋겠습니다. 자바스크립트의 내부에 대해서 궁금하시면 오픈소스 프로젝트인 V8에 대한 참고 문서가 많으니 꼭 한번 둘러보세요.

This entry was posted in 기타 and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*


구글코리아