Skip to content

Commit 11982ae

Browse files
apapirovskievanlucas
authored andcommitted
process: JS fast path for bindings
Currently, both process.binding and internalBinding have to call into C++ regardless of whether the module has been cached or not. This creates significant overhead to all binding calls and unfortunately the rest of the codebase doesn't really optimize the amount of times that bindings are required (as an example: 12 files require the async_wrap binding). Changing all the usage of this function throughout the codebase would introduce a lot of churn (and is kind of a hassle) so instead this PR introduces a JS fast path for both functions for cases where the binding has already been cached. While micro benchmarks are not super meaningful here (it's not like we call binding that often...), this does speed up the cached call by 400%. In addition, move moduleLoadList creation and management entirely into JS-land as it requires less code and is more efficient. PR-URL: #18365 Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
1 parent 144cfb4 commit 11982ae

File tree

4 files changed

+55
-78
lines changed

4 files changed

+55
-78
lines changed

lib/internal/bootstrap_node.js

+49-4
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121

2222
setupProcessObject();
2323

24-
internalBinding = process._internalBinding;
25-
delete process._internalBinding;
26-
2724
// do this good and early, since it handles errors.
2825
setupProcessFatal();
2926

@@ -245,6 +242,54 @@
245242
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
246243
}
247244

245+
const moduleLoadList = [];
246+
Object.defineProperty(process, 'moduleLoadList', {
247+
value: moduleLoadList,
248+
configurable: true,
249+
enumerable: true,
250+
writable: false
251+
});
252+
253+
{
254+
const bindingObj = Object.create(null);
255+
256+
const getBinding = process.binding;
257+
process.binding = function binding(module) {
258+
module = String(module);
259+
let mod = bindingObj[module];
260+
if (typeof mod !== 'object') {
261+
mod = bindingObj[module] = getBinding(module);
262+
moduleLoadList.push(`Binding ${module}`);
263+
}
264+
return mod;
265+
};
266+
267+
const getLinkedBinding = process._linkedBinding;
268+
process._linkedBinding = function _linkedBinding(module) {
269+
module = String(module);
270+
let mod = bindingObj[module];
271+
if (typeof mod !== 'object')
272+
mod = bindingObj[module] = getLinkedBinding(module);
273+
return mod;
274+
};
275+
}
276+
277+
{
278+
const bindingObj = Object.create(null);
279+
280+
const getInternalBinding = process._internalBinding;
281+
delete process._internalBinding;
282+
283+
internalBinding = function internalBinding(module) {
284+
let mod = bindingObj[module];
285+
if (typeof mod !== 'object') {
286+
mod = bindingObj[module] = getInternalBinding(module);
287+
moduleLoadList.push(`Internal Binding ${module}`);
288+
}
289+
return mod;
290+
};
291+
}
292+
248293
function setupProcessObject() {
249294
process._setupProcessObject(pushValueToArray);
250295

@@ -542,7 +587,7 @@
542587
throw err;
543588
}
544589

545-
process.moduleLoadList.push(`NativeModule ${id}`);
590+
moduleLoadList.push(`NativeModule ${id}`);
546591

547592
const nativeModule = new NativeModule(id);
548593

src/env-inl.h

-12
Original file line numberDiff line numberDiff line change
@@ -298,18 +298,6 @@ inline Environment::Environment(IsolateData* isolate_data,
298298
v8::Context::Scope context_scope(context);
299299
set_as_external(v8::External::New(isolate(), this));
300300

301-
v8::Local<v8::Primitive> null = v8::Null(isolate());
302-
v8::Local<v8::Object> binding_cache_object = v8::Object::New(isolate());
303-
CHECK(binding_cache_object->SetPrototype(context, null).FromJust());
304-
set_binding_cache_object(binding_cache_object);
305-
306-
v8::Local<v8::Object> internal_binding_cache_object =
307-
v8::Object::New(isolate());
308-
CHECK(internal_binding_cache_object->SetPrototype(context, null).FromJust());
309-
set_internal_binding_cache_object(internal_binding_cache_object);
310-
311-
set_module_load_list_array(v8::Array::New(isolate()));
312-
313301
AssignToContext(context, ContextInfo(""));
314302

315303
destroy_async_id_list_.reserve(512);

src/env.h

-3
Original file line numberDiff line numberDiff line change
@@ -281,15 +281,12 @@ class ModuleWrap;
281281
V(async_hooks_after_function, v8::Function) \
282282
V(async_hooks_promise_resolve_function, v8::Function) \
283283
V(async_hooks_binding, v8::Object) \
284-
V(binding_cache_object, v8::Object) \
285-
V(internal_binding_cache_object, v8::Object) \
286284
V(buffer_prototype_object, v8::Object) \
287285
V(context, v8::Context) \
288286
V(http2ping_constructor_template, v8::ObjectTemplate) \
289287
V(http2stream_constructor_template, v8::ObjectTemplate) \
290288
V(http2settings_constructor_template, v8::ObjectTemplate) \
291289
V(inspector_console_api_object, v8::Object) \
292-
V(module_load_list_array, v8::Array) \
293290
V(pbkdf2_constructor_template, v8::ObjectTemplate) \
294291
V(pipe_constructor_template, v8::FunctionTemplate) \
295292
V(performance_entry_callback, v8::Function) \

src/node.cc

+6-59
Original file line numberDiff line numberDiff line change
@@ -2835,22 +2835,6 @@ Maybe<bool> ProcessEmitDeprecationWarning(Environment* env,
28352835
}
28362836

28372837

2838-
static bool PullFromCache(Environment* env,
2839-
const FunctionCallbackInfo<Value>& args,
2840-
Local<String> module,
2841-
Local<Object> cache) {
2842-
Local<Context> context = env->context();
2843-
Local<Value> exports_v;
2844-
Local<Object> exports;
2845-
if (cache->Get(context, module).ToLocal(&exports_v) &&
2846-
exports_v->IsObject() &&
2847-
exports_v->ToObject(context).ToLocal(&exports)) {
2848-
args.GetReturnValue().Set(exports);
2849-
return true;
2850-
}
2851-
return false;
2852-
}
2853-
28542838
static Local<Object> InitModule(Environment* env,
28552839
node_module* mod,
28562840
Local<String> module) {
@@ -2878,22 +2862,10 @@ static void ThrowIfNoSuchModule(Environment* env, const char* module_v) {
28782862
static void Binding(const FunctionCallbackInfo<Value>& args) {
28792863
Environment* env = Environment::GetCurrent(args);
28802864

2881-
Local<String> module;
2882-
if (!args[0]->ToString(env->context()).ToLocal(&module)) return;
2883-
2884-
Local<Object> cache = env->binding_cache_object();
2885-
2886-
if (PullFromCache(env, args, module, cache))
2887-
return;
2865+
CHECK(args[0]->IsString());
28882866

2889-
// Append a string to process.moduleLoadList
2890-
char buf[1024];
2867+
Local<String> module = args[0].As<String>();
28912868
node::Utf8Value module_v(env->isolate(), module);
2892-
snprintf(buf, sizeof(buf), "Binding %s", *module_v);
2893-
2894-
Local<Array> modules = env->module_load_list_array();
2895-
uint32_t l = modules->Length();
2896-
modules->Set(l, OneByteString(env->isolate(), buf));
28972869

28982870
node_module* mod = get_builtin_module(*module_v);
28992871
Local<Object> exports;
@@ -2910,50 +2882,31 @@ static void Binding(const FunctionCallbackInfo<Value>& args) {
29102882
} else {
29112883
return ThrowIfNoSuchModule(env, *module_v);
29122884
}
2913-
cache->Set(module, exports);
29142885

29152886
args.GetReturnValue().Set(exports);
29162887
}
29172888

29182889
static void InternalBinding(const FunctionCallbackInfo<Value>& args) {
29192890
Environment* env = Environment::GetCurrent(args);
29202891

2921-
Local<String> module;
2922-
if (!args[0]->ToString(env->context()).ToLocal(&module)) return;
2923-
2924-
Local<Object> cache = env->internal_binding_cache_object();
2925-
2926-
if (PullFromCache(env, args, module, cache))
2927-
return;
2892+
CHECK(args[0]->IsString());
29282893

2929-
// Append a string to process.moduleLoadList
2930-
char buf[1024];
2894+
Local<String> module = args[0].As<String>();
29312895
node::Utf8Value module_v(env->isolate(), module);
2932-
snprintf(buf, sizeof(buf), "Internal Binding %s", *module_v);
2933-
2934-
Local<Array> modules = env->module_load_list_array();
2935-
uint32_t l = modules->Length();
2936-
modules->Set(l, OneByteString(env->isolate(), buf));
29372896

29382897
node_module* mod = get_internal_module(*module_v);
29392898
if (mod == nullptr) return ThrowIfNoSuchModule(env, *module_v);
29402899
Local<Object> exports = InitModule(env, mod, module);
2941-
cache->Set(module, exports);
29422900

29432901
args.GetReturnValue().Set(exports);
29442902
}
29452903

29462904
static void LinkedBinding(const FunctionCallbackInfo<Value>& args) {
29472905
Environment* env = Environment::GetCurrent(args.GetIsolate());
29482906

2949-
Local<String> module_name;
2950-
if (!args[0]->ToString(env->context()).ToLocal(&module_name)) return;
2951-
2952-
Local<Object> cache = env->binding_cache_object();
2953-
Local<Value> exports_v = cache->Get(module_name);
2907+
CHECK(args[0]->IsString());
29542908

2955-
if (exports_v->IsObject())
2956-
return args.GetReturnValue().Set(exports_v.As<Object>());
2909+
Local<String> module_name = args[0].As<String>();
29572910

29582911
node::Utf8Value module_name_v(env->isolate(), module_name);
29592912
node_module* mod = get_linked_module(*module_name_v);
@@ -2984,7 +2937,6 @@ static void LinkedBinding(const FunctionCallbackInfo<Value>& args) {
29842937
}
29852938

29862939
auto effective_exports = module->Get(exports_prop);
2987-
cache->Set(module_name, effective_exports);
29882940

29892941
args.GetReturnValue().Set(effective_exports);
29902942
}
@@ -3319,11 +3271,6 @@ void SetupProcessObject(Environment* env,
33193271
"version",
33203272
FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION));
33213273

3322-
// process.moduleLoadList
3323-
READONLY_PROPERTY(process,
3324-
"moduleLoadList",
3325-
env->module_load_list_array());
3326-
33273274
// process.versions
33283275
Local<Object> versions = Object::New(env->isolate());
33293276
READONLY_PROPERTY(process, "versions", versions);

0 commit comments

Comments
 (0)