이 간단한 jsperf 테스트 의 결과에 놀랐습니다 .
Benchmark.prototype.setup = function() {
var O = function() {
this.f = function(){};
}
var o = new O();
var o2 = {
f : function(){}
};
};
// Test case #1
o.f(); // ~721M ops/s
// Test case #2
o2.f(); // ~135M ops/s
나는 둘 다 동일한 성능을 기대했습니다 (실제로 Firefox에서도 성능이 비슷합니다). V8은 케이스 # 1에서 무언가를 최적화해야합니다.
V8 및 jsPerf에 대한 첫 번째 기본 사항 :
V8은 숨겨진 클래스 라는 기술을 사용합니다 . 각 히든 클래스는 특정 오브젝트 모양을 설명합니다. 예를 들어 오브젝트는 x
오프셋에 속성 이16
있거나 오브젝트가 메소드를f
가지며 이러한 히든 클래스는 오브젝트가 변형되어 전환 트리 (엄밀히 말하면 dags)를 형성 할 때 전환 과 함께 연결됩니다 . 모든 숨겨진 클래스가 동일한 전환 트리에있는 것은 아닙니다. 대신 각 생성자에서 새로운 전환 트리가 생성됩니다. 숨겨진 클래스의 기본 개념을 이해하려면 이 슬라이드 를 살펴보십시오 .
jsPerf는 다음과 같은 작업을 수행 할 때 테스트를 실행합니다 : 부여 setup
하고 body
그것을 여러 번 생성하고이 같은 약을 찾고 함수를 실행합니다 :
function measure() {
/* setup */
var start = Date.now();
for (var i = 0; i < N; i++) {
/* body */
}
var end = Date.now();
/* N / (start - end) determines ops / ms reported */
}
각 실행을 샘플 이라고합니다 .
이제 벤치 마크에서 전환 트리를 살펴 보겠습니다.
의 숨겨진 클래스는 o
constructor에 루트가있는 전환 트리 에 속합니다 O
. 각 생성자는 한 번만 호출됩니다. 이를 통해 V8은 메모리에 다음과 같은 전환 트리를 구축 할 수 있습니다.
O{} -f-> O{ f: <closure> }
의 히든 클래스는 o
기본적 으로 주어진 클로저에 의해 구현 된 메소드o
가 있음 을 V8에 알려줍니다 . 이를 통해 V8의 최적화 컴파일러가 위의 벤치 마크에서 인라인 할 수 있으므로 벤치마킹 루프가 비워집니다.f
f
의 숨겨진 클래스는 o2
의 전환 트리에 속합니다 Object
. 먼저 통지 setup
V8 홍보와 같은 최적화를 적용하려고 그래서 만약 여러 번 호출 f
이 불가능한 전환 트리에 도착할 것입니다 방법에 :
Object{} -f-> Object{ f: <closure> }
-f-> Object{ f: <other closure> }
사실 V8은 시도조차하지 않습니다. V8 구현 f
자는 이 상황을 예견했고 V8 은 정상적인 속성을 만듭니다 .
Object{} -f-> Object{ f: <property at offset 8> }
따라서 호출 o2.f()
하려면 먼저로드해야하며 이로 인해 인라인도 손상됩니다.
여기서 한 가지 중요한 사실을 깨달아야합니다. O
생성자를 두 번 호출 하면 V8은 V8이 타격을 피하는 동일한 불가능한 전환 트리에 도달합니다 Object
.
O{} -f-> O{ f: <closure> }
-f-> O{ f: <other closure> }
당신은 그런 구조를 가질 수 없습니다. 이 경우 V8 on the fly는 f
메서드를 만드는 대신 필드 로 변환 하고 전환 트리를 다시 작성합니다.
O{} -f-> O{ f: <property at offset 8> }
http://jsperf.com/function-call-on-js-objects/3 에서이 효과를 확인하십시오. 여기서 new O()
생성하기 전에 추가했습니다 o
. 객체 리터럴과로 구성된 객체의 성능 new
이 동일 하다는 것을 알 수 있습니다.
여기서 또 다른 세부 사항 f
은 리터럴이 전역 범위 에서 선언 된 경우 V8이 리터럴에 대한 메서드 로도 전환하려고 시도한다는 것 입니다. V8에 대한 http://jsperf.com/function-call-on-js-objects/5 및 Issue 2246을 참조하십시오 . 이유는 간단합니다. 전역 범위의 리터럴은 한 번만 평가되므로 그러한 승격이 성공할 가능성이 높고 리터럴이 여러 번 평가되는 경우 발생하는 메서드간에 충돌이 발생하지 않습니다.
내 블로그 게시물 에서 유사한 문제에 대해 자세히 읽을 수 있습니다 .
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다