자바스크립트는 class
의 개념이 없고 prototype
이라는 개념이 있어 객체간의 상속이 가능하다.class
기반의 언어는 클래스에 속한 객체들이 모두 같은 필드 구조를 가진다.
어떤 객체가 특정 프로퍼티에 접근할 때 어떤 변수에 접근하는지, 메모리의 어느 위치를 참조하면 되는지 컴파일 과정에서 미리 알 수 있다.
offset값을 통해 바로 멤버변수에 접근가능하다.
그러나 자바스크립트에는 클래스라는 개념이 존재하지 않으며 동적인 언어이기 때문에
언제 바뀌는지 알 수 없어 각 객체들마다 필드구조를 각각 가지고 있어야한다.
(중복되는 객체가 100개여도 100개의 필드구조)
그러므로 성능상의 문제가 생기게 된다.
V8 엔진
자바스크립트의 성능상 이슈를 V8
엔진이 해결을 해준다.V8
엔진은 자바스크립트 엔진으로 google에서 만들었고 chrome, safari 에서 사용한다.
인터프리터를 사용하지않고 머신 코드로 변환하여 속도가 빠르다는 장점이 있다.
대표적인 특징으로는 hidden class와 inline caching이 있다.
이러한 특징으로 인해 자바스크립트 성능을 최적화하는데 도움을 준다고 한다.
hidden class
자바스크립트에는 클래스의 개념은 없지만 엔진 안쪽에 숨겨진 hidden class라는 것이 존재한다.1
let obj = {};
객체가 생성되면 히든클래스가 생성이 된다. 여기서는 C0라고 가정하겠다.1
2let obj = {};
obj.a = 1;
그리고 a라는 프로퍼티를 추가하게되면 C1이라는 히든클래스가 생성되면서 obj의 히든클래스는 C1으로 바뀌게 된다.
C0 히든클래스에는 a라는 프로퍼티가 추가되면 C1라는 히든 클래스도 이동한다라는 정보가 담기게 된다.
바로 이게 전환테이블(m_transitionTable)
이다.1
2
3let obj = {};
obj.a = 1;
obj.b = 2;
b 프로터티가 추가되면 마찬가리고 C2라는 히든 클래스가 생성되면 obj의 히든클래스가 C2로 변경된다.
그리고 C1에 전환 테이블에는 b라는 프로퍼티가 추가되면 C2라는 히든 클래스도 이동한다라는 정보가 담기게 된다.
1 | function foo(a) { |
이런경우에도 a에 대한 히든 클래스가 만들어지고 b도 동일 히든클래스를 공유하게 된다.
그러나 실제 필드에 접근해서 값을 가지고 오려면 객체 -> hidden class -> 프로퍼티 테이블 -> 프로퍼티 비교 -> [오브젝트 + 오프셋]
의 단계를
거쳐야 실제 필드에 접근할 수 있다.
필드에 접근하기 까지 많은 단계를 거쳐야하기 때문에 성능에 영향을 미칠 수 있다.
하지만 이러한 작업은 inline caching을 위한 준비이다.
inline caching
객체 필드에 접근할 때 hidden class를 사용한다면 결국 최종 목적은 필드의 오프셋값에 접근하는 것이다.
바로 여기서 inline caching
은 이 오프셋 값을 캐싱하겠다는 의미이다.
자바스크립트는 동적언어이지만 실제로는 안바뀌는게 더 많고 성능을 빠르게 하려면 루프를 돌려야한다는 가정을 하자.
1 | for (var i=0; i<10; i++) { |
이렇게 루프문을 돌리면 i=0일 때는 캐싱된 값이 없어 느리지만 i=1부터는 값이 오프셋 값이 캐싱되어 클래스 기반의 언어와 똑같은 성능을 보이게 된다.
단, arr[1]부터 arr[9]까지가 모든 같은 필드 구조를 가지고 있어야만 성립되는 내용이다.
추가로 첫번째 수행에서 바로 캐싱되지 않고 두번 수행된 코드부터 캐싱이 된다. 이는 한번 수행된 코드는 한번만 수행될 가능성 높지만 두번 수행된 코드는 이후에 또 수행될 확률이 높기 때문이라고 한다.
인라인 캐싱의 포인트는 동일한 유형의 객체가 동일한 히든 클래스를 공유한다는 것이다.
만약 다른 프로퍼티가 중간에 추가되거나 하면 inline caching을 더이상 사용할 수 없다.
최적화 계획
히든 클래스를 서로 공유할 수 있게끔 객체를 만들어야한다.
만약 객체가 생성된 후 중간에 프로퍼티를 추가하게 되면 히든 클래스가 변경된다.
아래의 코드와 같이 생성자에서 모든 프로퍼티를 할당할 수 있도록 하는 것이 좋다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function Foo(a,b) {
this.x = a;
this.y = b;
}
let a = new Foo(1, 2);
a.z = 1; // 최적화 X
function Bar(a,b, c) {
this.x = a;
this.y = b;
this.z = c;
}
let a = new Bar(1, 2, 3); // 최적화 O