V8의 JavaScript 최적화
V8 엔진이 어떻게 JS를 최적화하는지 이해하고 이를 기반으로 성능적인 면에서 좋은 코드를 작성할 수 있는 방법을 알아보자.
JavaScript 컴파일
V8는 두가지 컴파일러를 사용한다고 한다.
Full Compiler - 일반적인 Javascript를 좋은 코드로 변환
- 여러 데이터 처리시 성능이 떨어질 수 있다.(하나의 type유지)
 - 따라서 다형적인 연산보다 단형적인 연산을 지향한다.
 - 단형적인 연산을 함으로써 하나의 Hidden Class를 공유할 수 있다. 
1function add (x, y) {2return x + y3}45add(1, 2); //type이 number6add("a", "b"); //type이 문자열로 바뀌었다. => 다형적 연산 
Optimizing Compiler - 대부분의 Javascript들을 뛰어난 코드로 변환하지만 시간 더 오래걸린다.
- Full Compiler와는 병렬로 동작하며 V8엔진이 자주 실행하는 함수를 재컴파일한다.
 - 단형적 함수와 생성자들은 완전히 인라인될 수 있다.
 - try {} catch {} 구문을 처리하지 않으므로 성능에 예민한 코드는 메소드로 선언하여 호출하는 방식을 권장한다.
 
히든클래스
- V8은 런타임시 객체의 내부적인 처리를 위해 히든클래스를 생성하여 사용한다.
 - 객체의 히든클래스가 동일한 경우 하나로 공유할 수 있다.
 - 따라서 객체를 이용할 경우 되도록 객체가 생성된 시점에서 속성을 추가하지 않도록 한다.
 - 추가하더라도 인스턴스를 여러개 사용할 경우 속성의 값 할당순서를 일치시키도록 한다.
1function Point(x, y) {2this.x = x;3this.y = y;4}56var p1 = new Point(11, 22);7var p2 = new Point(33, 44);8// 여기에서 p1과 p2는 hidden class를 공유910p2.z = 55; // p1과 p2는 이제 다른 hidden class를 갖게된다11p1.z = 60; // p1에 p2와 같은 속성을 추가하면서 p1과 p2는 다시 같은 hidden class를 공유한다 
인라이닝
- 함수의 호출 지점에 해당 함수의 내용으로 바꾸어 주는 역할을 한다.
 - 인라인캐싱의 영향으로 동일한 메소드를 반복적으로 수행하는 코드가 서로 다른 메소드를 한 번씩만 수행하는 코드보다 더 빠르게 동작한다.
 - 반복코드는 재사용하는것이 좋다.
 
Number 타입
- V8은 데이터 타입 변환시 값을 효율적으로 나타내는 태그를 사용한다. 사용자가 사용하는 값을 추론하여 number의 타입을 추론한다.
 - 31비트 부호있는 정수를 할당할 경우 number, 부동 소수를 사용할 경우 double 타입으로 변하게 된다.
 - 따라서 되도록 number타입을 사용하고 데이터가 부동소수로 변하지 않게 유의하도록 한다.
1var i = 10; // 31비트 부호있는 정수,2var j = 10.2; // 이 값은 double 타입의 부동 소수점 숫자 데이터이다. 
배열처리
- Normal Array 유형
- 일반적인 배열처리를 위해 두가지 유형의 배열 저장소가 존재한다.
 - Fast Elements - 키 값이 순서대로 채워진 경우 사용되는 선형 저장소
 - Dictionary Elements - 나머지 경우에 사용되는 해쉬 테이블 저장소
 - 가능하면 한 유형에서 다른유형으로 전환하지 않도록 유자하도록 한다. 따라서 키값이 순서대로 채워진 배열상태를 유지하는 것을 지향한다.
- 인덱스를 0부터 시작한다.
 - 배열 선언시 처음부터 큰 값을 할당하지 않고 차츰 늘려가는 방식으로 작업한다.
 - 숫자 배열의 요소를 삭제하지 않는다.
 - 초기화하지 안한 요소는 호출하지 않는다.
 
 
 - double Array 유형 
- 더블타입 배열이 일반 배열보다 빠르다.(일반타입은 요소별로 타입을 검사하여 히든클래스를 변경하는 작업이 있었으나, 더블타입은 여기서 제외된다.)
1// 비효율적인 코드2var a = new Array();3a[0] = 77; // 할당4a[1] = 88;5a[2] = 0.5; // 할당, 배열 타입 변환 (일반배열 -> double 배열)6a[3] = true; // 할당, 배열 타입 변환 (double 배열 -> 일반배열)78// 효율적인 코드9var a = [77, 88, 0.5, true]; - 배열요소 추가작업이 성능에 영향을 줄 수 있다. 따라서, 한번에 배열을 할당하게 되면 컴파일러가 모든 요소를 알고 히든클래스를 미리 정할 수 있다.
 
 - 더블타입 배열이 일반 배열보다 빠르다.(일반타입은 요소별로 타입을 검사하여 히든클래스를 변경하는 작업이 있었으나, 더블타입은 여기서 제외된다.)
 
V8엔진에 최적화된 코드 작성을 위한 팁 정리
- 단형적인 연산을 지향하고 되도록 타입이 변하지 않도록 한다.
 - 최적화 컴파일러는 try {} catch {}구문을 처리하지 않으므로, 성능에 예민한 코드는 함수화 하여 호출하도록 한다.
 - 객체를 생성하여 사용하는 경우 동일한 히든클래스를 공유하도록 하자.  
- 객체 속성의 순서: 객체 속성을 항상 같은 순서로 초기화해서 히든클래스 및 이후에 생성되는 최적화 코드가 공유될 수 있도록 한다.
 - 동적 속성: 객체 생성 이후에 속성을 추가하는 것은 히든 클래스가 변하도록 강제하고 이전의 히든클래스를 대상으로 최적화되었던 모든 메소드를 느리게 만든다. 대신에 모든 객체의 속성을 생성자에서 할당하도록 하자.
 
 - 메소드를 재사용을 지향하자. 동일한 메소드를 반복적으로 수행하는 코드가 서로 다른 메소드를 한 번씩만 수행하는 코드 보다 더 빠르게 동작한다.
 - Number타입은 가능한한 정수(31비트의 숫자)를 사용하자.
 - 배열
- 값이 띄엄띄엄 있어서 키가 계속해서 증가하는 숫자가 되지 않는 배열은 피하자.
 - 모든 요소를 가지지는 않는 배열의 요소들은 접근하기에 많은 비용이 든다.
 - 커다란 배열을 미리 할당하지 않도록 한다. 사용하면서 크기가 커지도록 하는 게 낫습니다.
 - 마지막으로 배열의 요소를 삭제하지 않는다. 그 배열의 키가 띄엄띄엄 배치되지 않도록 하기 위함.
 - 배열의 요소별로 여러 타입의 데이터가 할당이 될 경우 한번에 배열에 할당하는것이 성능적으로 좋다.
 
 - 선언한 변수의 타입이 되도록 변형되지 않도록 주의하자.
 
![]()

