[javascript] Bluebird의 util.toFastProperties 함수는 어떻게 객체의 속성을 “빠르게”만드는가?
Bluebird의 util.js
파일 에는 다음과 같은 기능이 있습니다.
function toFastProperties(obj) {
/*jshint -W027*/
function f() {}
f.prototype = obj;
ASSERT("%HasFastProperties", true, obj);
return f;
eval(obj);
}
어떤 이유로 반환 함수 뒤에 문이 있는데 왜 거기에 있는지 잘 모르겠습니다.
또한 저자가 이것에 대해 JSHint 경고를 침묵 시켰기 때문에 의도적 인 것으로 보입니다.
‘반품’후 도달 할 수없는 ‘평가’. (W027)
이 기능은 정확히 무엇을합니까? 않습니다 util.toFastProperties
정말 “빠른”객체의 속성을?
Bluebird의 GitHub 리포지토리를 통해 소스 코드에 대한 의견이나 문제 목록에 대한 설명을 찾았지만 찾을 수 없었습니다.
답변
2017 업데이트 : 먼저 오늘 독자들을 위해-Node 7 (4+)에서 작동하는 버전이 있습니다.
function enforceFastProperties(o) {
function Sub() {}
Sub.prototype = o;
var receiver = new Sub(); // create an instance
function ic() { return typeof receiver.foo; } // perform access
ic();
ic();
return o;
eval("o" + o); // ensure no dead code elimination
}
Sans 하나 또는 두 개의 작은 최적화-아래의 모든 내용이 여전히 유효합니다.
먼저 그것이 무엇을하고 왜 더 빠르며 왜 작동하는지에 대해 논의 해 봅시다.
그것이하는 일
V8 엔진은 두 가지 객체 표현을 사용합니다.
다음은 속도 차이를 보여주는 간단한 데모 입니다. 여기서는 delete
문장을 사용하여 객체를 느린 사전 모드로 만듭니다.
엔진은 가능할 때마다 그리고 일반적으로 많은 속성 액세스가 수행 될 때마다 빠른 모드를 사용하려고 시도하지만 때로는 사전 모드로 전환됩니다. 사전 모드에 있으면 성능이 크게 저하되므로 일반적으로 객체를 빠른 모드로 설정하는 것이 좋습니다.
이 핵은 개체를 사전 모드에서 빠른 모드로 강제 설정하기위한 것입니다.
- 블루 버드의 페트 카 자신 도 여기서 이야기합니다 .
- Vyacheslav Egorov 의이 슬라이드 에서도 언급합니다.
- 이 질문과 그 답변 도 관련이 있습니다.
- 이 약간 오래된 기사 는 v8에서 객체를 저장하는 방법에 대한 좋은 아이디어를 제공하는 상당히 좋은 기사 입니다.
왜 더 빠른가
JavaScript 프로토 타입에서 일반적으로 많은 인스턴스간에 공유되는 함수를 저장하고 동적으로 많이 변경하는 경우는 거의 없습니다. 이러한 이유로 함수가 호출 될 때마다 추가 패널티를 피하기 위해 빠른 모드로 설정하는 것이 매우 바람직합니다.
이를 위해 v8은 .prototype
함수의 생성자 인 함수를 호출하여 생성 된 모든 개체가 공유하기 때문에 함수 의 속성 인 개체를 빠른 모드로 설정합니다. 이것은 일반적으로 영리하고 바람직한 최적화입니다.
작동 원리
먼저 코드를 살펴보고 각 줄이 무엇을하는지 알아 봅시다.
function toFastProperties(obj) {
/*jshint -W027*/ // suppress the "unreachable code" error
function f() {} // declare a new function
f.prototype = obj; // assign obj as its prototype to trigger the optimization
// assert the optimization passes to prevent the code from breaking in the
// future in case this optimization breaks:
ASSERT("%HasFastProperties", true, obj); // requires the "native syntax" flag
return f; // return it
eval(obj); // prevent the function from being optimized through dead code
// elimination or further optimizations. This code is never
// reached but even using eval in unreachable code causes v8
// to not optimize functions.
}
우리는하지 않습니다 가 , 우리가 대신 최적화 할 수 않는 V8을 주장 코드를 자신을 찾기 위해 V8 엔진의 단위 테스트를 읽기 :
// Adding this many properties makes it slow.
assertFalse(%HasFastProperties(proto));
DoProtoMagic(proto, set__proto__);
// Making it a prototype makes it fast again.
assertTrue(%HasFastProperties(proto));
이 테스트를 읽고 실행하면이 최적화가 실제로 v8에서 작동 함을 알 수 있습니다. 그러나-방법을 보는 것이 좋을 것입니다.
확인 objects.cc
하면 다음 기능 (L9925)을 찾을 수 있습니다.
void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
if (object->IsGlobalObject()) return;
// Make sure prototypes are fast objects and their maps have the bit set
// so they remain fast.
if (!object->HasFastProperties()) {
MigrateSlowToFast(object, 0);
}
}
이제 JSObject::MigrateSlowToFast
사전을 명시 적으로 가져 와서 빠른 V8 개체로 변환합니다. v8 객체 내부에 대한 가치있는 읽기와 흥미로운 통찰력이지만 여기서는 주제가 아닙니다. v8 객체에 대해 배우는 좋은 방법이므로 계속 읽으십시오 .
SetPrototype
에서 체크 아웃하면 objects.cc
12231 행에서 호출 된 것을 볼 수 있습니다.
if (value->IsJSObject()) {
JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}
어느 FuntionSetPrototype
것이 우리가 얻는 것입니다 .prototype =
.
수행 __proto__ =
했거나 .setPrototypeOf
작동했지만 ES6 기능이며 Bluescape는 Netscape 7 이후 모든 브라우저에서 실행되므로 여기서 코드를 단순화하는 것은 의문의 여지가 없습니다. 예를 들어 확인하면 .setPrototypeOf
다음을 볼 수 있습니다.
// ES6 section 19.1.2.19.
function ObjectSetPrototypeOf(obj, proto) {
CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");
if (proto !== null && !IS_SPEC_OBJECT(proto)) {
throw MakeTypeError("proto_object_or_null", [proto]);
}
if (IS_SPEC_OBJECT(obj)) {
%SetPrototype(obj, proto); // MAKE IT FAST
}
return obj;
}
어느쪽에 직접 있습니다 Object
:
InstallFunctions($Object, DONT_ENUM, $Array(
...
"setPrototypeOf", ObjectSetPrototypeOf,
...
));
그래서 우리는 Petka가 작성한 코드에서 베어 메탈까지의 길을 걸었습니다. 이것은 좋았다.
부인 성명:
이것은 모든 구현 세부 사항입니다. Petka와 같은 사람들은 최적화 괴물입니다. 조기 최적화는 97 %의 모든 악의 근원이라는 것을 항상 기억하십시오. Bluebird는 매우 기본적인 작업을 매우 자주 수행하므로 이러한 성능 해킹을 통해 많은 이점을 얻을 수 있습니다. 콜백만큼 빠르지는 않습니다. 당신은 거의 도서관에 전원을 공급하지 않는 코드 같은 것을 할 필요가 없습니다.