[JavaScript] 히든클래스, 인라인 캐싱 그리고 최적화

10570 단어 JavaScriptJavaScript

인라인 캐싱 (inline caching)

인라인 캐싱이란 컴파일하는 과정에서 수행되는 기능이다. 일단 이름만 보고 이게 무엇인지 추측해보자
먼저 C나 C++ 언어에서 인라인 함수라는 문법이 있다. 이는 함수의 호출을 함수 그 자체 내용의 복사본으로 직접 치환하여 오버헤드를 없애는 기법이다.
다음으로 캐싱이란 캐시에 자주 쓰이는 데이터를 저장해 놓고 빠른 속도로 메모리에 가져와서 속도 향상을 목표하는 기법이다.
이를 통해서 인라인 캐싱이란 캐싱을 사용하여 인라인 함수를 구현한 방법 이라고 유추해 볼 수 있겠다.

function findUser(user){
  return `found ${user.firstName} ${user.lastName}`
}

const userData = {
  firstName : 'johnson'
  lastName : 'junior'
}

findUser(userData)

위의 코드에 인라인 캐싱을 적용한다고 하면 컴파일러는 userData 객체를 반복해서 찾는 대신 found ${user.firstName} ${user.lastName}found johnson junior 라는 문자열으로 대체하여 실행 속도를 향상시킨다.


히든 클래스 (Hidden class)

자바스크립트에는 클래스의 개념이 없(었)지만 엔진 안쪽에 숨겨진 클래스 개념을 두는 최적화 기법이 있다.
글로 설명하기보단 바로 예시를 들어 다음과 같은 코드가 있다고 해보자.

1  function Point(x,y) {
2  	this.x = x;
3	this.y = y;
4  }
5
6  var obj = new Point(1,2);

1) 6번째 줄에서 새로운 생성자를 만들고 선언될 때, 자바스크립트 엔진(V8) 에서는 히든 클래스 C0를 생성한다. 그리고 생성된 obj 객체는 C0를 참조하게 된다.

2) 그 다음으로 Point function 에서 this.x = x 가 실행되면 V8엔진은 히든클래스 C1을 만들고 클래스 전환(class transition) 을 통해서 업데이트 한다.
Transition Table 이란 런타임에 필드 구조가 변하면 이쪽으로 가세요 라고 말해주는 이정표와 같다. 예시에서는 Property x 가 추가되었기 때문에 C1 히든 클래스를 생성하고 이를 가리키는 포인터를 생성하여 클래스 전환을 하였다.
Property Table은 Property 가 메모리의 어디에 있는지 찾기 쉽도록 알려주는 이정표와 같다. 그림에서 X는 Offset 0번에 저장되어 있고 연속적인 메모리 버퍼에서 0번째 속성값은 x로 지정된 것임을 나타낸다.

3) 2번과 동일하게 Point function 에서 this.y = y 가 실행되었고 V8엔진은 히든클래스 C2를 만들고 클래스 전환(class transition) 을 통해서 업데이트 하였다.

그렇다면 궁극적인 목표인 코드 최적화를 위해 우리가 할 수 있는 일에 대해서 알아보자.
우선 다음과 같은 코드가 있다고 하고 동일하게 그림으로 알아보자

1    function Animal(k){
2      this.x = k;
3    } 
4
5   const obj1 = new Animal(1);
6   const obj2 = new Animal(2);
7
8   obj1.a = 30;
9   obj1.b = 100;
10 
11  obj2.b = 100;
12  obj2.a = 30;

그리는 과정은 생략하고 결과적으로 아래와 같은 결과가 나올 것이다.

그림에서 확인할 수 있듯 객체 obj1obj2 는 결과적으로 동일한 데이터를 가지고 있지만 결과적으로 다른 히든 클래스를 참조하게 된다.
딱 보기에도 조잡해 보이는 이러한 구조는 성능적인 이슈를 야기하는데, 바로 위에서 언급했던 인라인 캐싱과 관련이 있다. 그렇다면 어떤 문제가 발생하고 어떻게 해결할 수 있는지 알아보자.


최적화

설명하기에 앞서 운영체제나 컴퓨터 구조를 공부하다보면 데이터의 지역성 이라는 단어를 자주 접하는데 인라인 캐싱도 이러한 특성을 이용한다. 동일한 메소드에 대한 반복적인 호출은 비슷한 유형의 객체에서 발생한다 라는 특성에서 기인한 것인데, 위와 같이 객체들이 다른 히든 클래스를 참조하고 있다면 인라인 캐싱 기법을 사용하는데 문제가 발생한다.

V8엔진에서는 인라인 캐싱을 사용할 경우 이전에 조회했던 오프셋을 사용하여 특정된 속성의 메모리 주소로 바로 이동할 수 있도록 하는데 다른 히든클래스를 참조할 경우 같은 데이터를 가지고 있다 할지라도 오프셋이 다르므로 인라인 캐싱을 사용할 수 없기 때문이다.

그러므로 지금까지의 내용을 종합해보면 생성자에서 모든 객체의 속성을 할당할 수 있도록 하는것이 좋겠다.

//발적화
function optimize(a, b) {
	this.a = a;
	this.b = b;
}var bad = new optimize(1,2);
bad.c = 3;//최적화
function optimize(a, b, c) {
	this.a = a;
	this.b = b;
	this.c = c;
}var good = new Test(1,2,3);

좋은 웹페이지 즐겨찾기