(최종 프로젝트 마무리 중)
보던 페이지:
V8 엔진이란? (’node인데 얘가 빌드(컴파일)을 하는 게 맞나?’) - https://1000hg.tistory.com/48
※ 이하는 스스로 공부하며 적어둔 노트이며 불확실한 내용이 있을 수 있습니다. 학습용으로 적합하지 않음을 유념해주세요. ※
V8 엔진
: Google의 오픈소스 자바스크립트 엔진. C++로 작성됐고 구글 크롬, 크로뮴 웹 브라우저와 Node.js에 쓰임.
컴파일을 한다는 것은 코드를 기계어로 변환한다는 것이다.
당연히 운영체제에 맞는 기계어여야 할 터이므로 컴파일된 프로그램은 OS에 종속적이다.
자바스크립트 런타임인 Node.js에 v8 엔진이 사용된 이유는 속도 개선때문이다.
기존의 (웹에 부착된)자바스크립트 엔진은 동적 인터프리터 방식을 사용했는데 이는 코드 크기가 커질수록 속도가 느려진다는 단점이 있다.
v8 엔진은 자바에서의 JVM과 비슷한 JIT(Just-In-Time) 컴파일러를 적용하여 느린 속도를 개선했다.
JIT 컴파일러의 특징은
인터프리터와 컴파일을 혼합한 방식이다. 코드를 실행한 시점에 인터프리터 방식으로 한 줄 한 줄 기계어를 생성하면서 동시에 캐싱하여 같은 함수가 여러 번 호출될 때 같은 기계어 코드를 반복 생성하는 것을 방지한다.
OS에 종속적이지 않다.
다른 순수 컴파일러만큼 속도가 빠르면서 인터프리터가 결합되어 유연성도 갖추고 있다.
런타임 시에 모든 코드를 기계어로 변환하고 중간 코드를 생성하지 않는다.
어떻게 일하나:
- Parser가 자바스크립트 소스코드를 AST로 변환
- Ignition 인터프리터가 AST를 Bytecode로 변환(런타임시 최초 1회). 이렇게 바이트 코드를 실행하며 프로그램을 가동시키다가 자주 사용되는 코드를 TurboFan으로 보냄.
- Turbofan 컴파일러가 이 Bytecode를 더 최적화된 코드로 변환. 다시 사용이 덜 되는 코드는 Deoptimizing 하게 된다.
AST (Abstract Syntax Tree)
: 컴파일러가 소스코드를 추상화된 구조로 만드는데 사용되는 자료 구조. 대부분의 프로그래밍 언어가 고차원 언어를 저차원 언어로 변환하는 데 이 AST들을 사용한다.
변환된 AST에는 변수 타입, 위치한 곳, 실행문의 순서 등의 정보가 담긴다. 주석 등은 이 시점에서 제외되므로 컴파일러는 주석을 맞닥뜨리지 않게 된다.
온라인 parsing tool도 이용해볼 수 있다. esprima 등을 이용하면 내가 작성한 자바스크립트 코드가 변환된 AST를 받아볼 수 있다.
예를 들어 이런 자바스크립트 함수를 작성하고 파싱하게 되면
// Function declaration
function addition(x, y){
// const HELLO = "HELLO!"
var answer = x + y;
console.log(answer);
}
// Calling the function
addition(10,20);
이런 syntax tree를 얻게 된다
{ "type": "Program", "body": [ { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "addition" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ], "body": { "type": "BlockStatement", "body": [ { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "HELLO" }, "init": { "type": "Literal", "value": "HELLO!", "raw": "\"HELLO!\"" } } ], "kind": "const" }, { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "answer" }, "init": { "type": "BinaryExpression", "operator": "+", "left": { "type": "Identifier", "name": "x" }, "right": { "type": "Identifier", "name": "y" } } } ], "kind": "var" }, { "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "MemberExpression", "computed": false, "object": { "type": "Identifier", "name": "console" }, "property": { "type": "Identifier", "name": "log" } }, "arguments": [ { "type": "Identifier", "name": "answer" } ] } } ] }, "generator": false, "expression": false, "async": false }, { "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "addition" }, "arguments": [ { "type": "Literal", "value": 10, "raw": "10" }, { "type": "Literal", "value": 20, "raw": "20" } ] } } ], "sourceType": "script" }
중요한 것은, 위에서부터 한 줄 한 줄 해석하면서 저 JSON 객체 같은 syntax tree에 키-밸류 쌍이 추가되게 된다는 것이다. 그래서 만약 주석 처리된 const HELLO 부분을 푼다면 이렇게 즉시로 중간에 추가되게 된다:
참고로 5.9버전 이전에 사용되던 Full-codegen과 Crankshaft는 (원래 의도대로) Ignition과 TurboFan이 완전히 제 성능을 내 줌에 따라 퇴장하게 되었다고 한다. 원래 의도는 Ignition과 TruboFan만을 사용하여 바이트 코드 ↔ 최적화된 코드 사이를 왔다갔다 하는 거였다고.
Parsing 파싱
: 소스코드를 불러온 후 컴퓨터가 분석하기 쉬운 형태인 AST(추상 구문 트리)로 변환하는 작업.
(계속)
Uploaded by N2T