바인딩이 클로저보다 느린 이유는 무엇입니까?
이전 포스터 에서 Javascript의 Function.bind vs Closure : 선택 방법을 물었습니다 .
그리고 부분적 으로이 답변을 받았습니다. 바인드가 클로저보다 빠르다는 것을 나타냅니다.
범위 순회는 다른 범위에있는 값 (변수, 객체)을 잡기 위해 도달 할 때 추가 오버 헤드가 추가된다는 의미입니다 (코드 실행 속도가 느려짐).
bind를 사용하면 기존 범위로 함수를 호출하므로 범위 순회가 발생하지 않습니다.
두 개의 jsperfs는 bind가 실제로 클로저 보다 훨씬 느리다는 것을 암시합니다 .
이것은 위의 코멘트로 게시되었습니다
그리고 저는 제 jsperf 를 작성하기로 결정했습니다.
그렇다면 왜 바인딩이 훨씬 느릴까요 (크롬에서 70 + %)?
더 빠르지 않고 클로저가 동일한 목적을 수행 할 수 있으므로 바인딩을 피해야합니까?
Chrome 59 업데이트 : 아래 답변에서 예상했듯이 새로운 최적화 컴파일러를 사용하면 bind가 더 이상 느려지지 않습니다. 세부 정보가있는 코드는 다음과 같습니다. https://codereview.chromium.org/2916063002/
대부분의 경우 중요하지 않습니다.
.bind
병목 현상이 있는 응용 프로그램을 만드는 경우 가 아니라면 신경 쓰지 않을 것입니다. 가독성은 대부분의 경우 순수한 성능보다 훨씬 더 중요합니다. 네이티브를 사용하면 .bind
일반적으로 더 읽기 쉽고 유지 관리가 가능한 코드가 제공 된다고 생각합니다 . 이는 큰 장점입니다.
그러나 예, 중요한 경우- .bind
더 느립니다.
예, .bind
클로저보다 상당히 느립니다. 적어도 Chrome에서는 v8
. 개인적으로 성능 문제로 인해 Node.JS로 전환해야했습니다 (보다 일반적으로 성능 집약적 인 상황에서는 클로저가 다소 느립니다).
왜? 때문에 .bind
알고리즘은 더 많은 다른 기능을 갖는 기능을 배치하고 사용하는 것보다 복잡 .call
하거나 .apply
. (재미있는 사실은 toString이 [native function]으로 설정된 함수도 반환합니다.)
스펙 관점과 구현 관점에서이를 보는 방법에는 두 가지가 있습니다. 둘 다 관찰합시다.
먼저 사양에 정의 된 bind 알고리즘을 살펴 보겠습니다 .
- Target을 this 값으로 둡니다.
- IsCallable (Target)이 false이면 TypeError 예외를 발생시킵니다.
- A를 thisArg 다음에 제공된 모든 인수 값 (arg1, arg2 등)의 순서대로 새로운 (비어있을 수 있음) 내부 목록으로 지정합니다.
...
(21. 인수 "arguments", PropertyDescriptor {[[Get]] : thrower, [[Set]] : thrower, [[Enumerable]] : false, [[Configurable])를 사용하여 F의 [[DefineOwnProperty]] 내부 메서드 호출) ] : false} 및 false.
(22. 반환 F.
랩보다 훨씬 더 복잡해 보입니다.
둘째, Chrome에서 어떻게 구현되는지 살펴 보겠습니다 .
FunctionBind
v8 (Chrome JavaScript 엔진) 소스 코드를 확인해 보겠습니다 .
function FunctionBind(this_arg) { // Length is 1.
if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
var boundFunction = function () {
// Poison .arguments and .caller, but is otherwise not detectable.
"use strict";
// This function must not use any object literals (Object, Array, RegExp),
// since the literals-array is being used to store the bound data.
if (%_IsConstructCall()) {
return %NewObjectFromBound(boundFunction);
}
var bindings = %BoundFunctionGetBindings(boundFunction);
var argc = %_ArgumentsLength();
if (argc == 0) {
return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
}
if (bindings.length === 2) {
return %Apply(bindings[0], bindings[1], arguments, 0, argc);
}
var bound_argc = bindings.length - 2;
var argv = new InternalArray(bound_argc + argc);
for (var i = 0; i < bound_argc; i++) {
argv[i] = bindings[i + 2];
}
for (var j = 0; j < argc; j++) {
argv[i++] = %_Arguments(j);
}
return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
};
%FunctionRemovePrototype(boundFunction);
var new_length = 0;
if (%_ClassOf(this) == "Function") {
// Function or FunctionProxy.
var old_length = this.length;
// FunctionProxies might provide a non-UInt32 value. If so, ignore it.
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--; // Don't count the thisArg as parameter.
new_length = old_length - argc;
if (new_length < 0) new_length = 0;
}
}
// This runtime function finds any remaining arguments on the stack,
// so we don't pass the arguments object.
var result = %FunctionBindArguments(boundFunction, this,
this_arg, new_length);
// We already have caller and arguments properties on functions,
// which are non-configurable. It therefore makes no sence to
// try to redefine these as defined by the spec. The spec says
// that bind should make these throw a TypeError if get or set
// is called and make them non-enumerable and non-configurable.
// To be consistent with our normal functions we leave this as it is.
// TODO(lrn): Do set these to be thrower.
return result;
We can see a bunch of expensive things here in the implementation. Namely %_IsConstructCall()
. This is of course needed to abide to the specification - but it also makes it slower than a simple wrap in many cases.
On another note, calling .bind
is also slightly different, the spec notes "Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties"
참고URL : https://stackoverflow.com/questions/17638305/why-is-bind-slower-than-a-closure
'Development Tip' 카테고리의 다른 글
실행 가능한 jar 라이브러리 처리 옵션의 차이점은 무엇입니까? (0) | 2020.10.16 |
---|---|
유효성 검사 오류 : 값이 유효하지 않습니다. (0) | 2020.10.16 |
git push to specific branch (0) | 2020.10.16 |
Valgrind는 어떻게 작동합니까? (0) | 2020.10.16 |
“Unparseable date : 1302828677828”서버에서받은 밀리 초 형식 날짜를 Gson으로 역 직렬화하려고합니다. (0) | 2020.10.16 |