diff options
author | Anna Henningsen <anna@addaleax.net> | 2020-02-01 19:16:23 +0300 |
---|---|---|
committer | Rich Trott <rtrott@gmail.com> | 2020-02-08 00:01:59 +0300 |
commit | a555be2e45b283a201de21350c0115680a7d4e7c (patch) | |
tree | c494be3c06a27acfc77b11ca4a0bb873fb331142 /src/node_messaging.cc | |
parent | e2a3a87060af4c48b2551d20dfede33183b1931d (diff) |
worker: improve MessagePort performance
Use a JS function as the single entry point for emitting `.onmessage()`
calls, avoiding the overhead of manually constructing each message
event object in C++.
confidence improvement accuracy (*) (**) (***)
worker/echo.js n=100000 sendsPerBroadcast=1 payload='object' workers=1 *** 16.34 % ±1.16% ±1.54% ±1.99%
worker/echo.js n=100000 sendsPerBroadcast=1 payload='string' workers=1 *** 24.41 % ±1.50% ±1.99% ±2.58%
worker/echo.js n=100000 sendsPerBroadcast=10 payload='object' workers=1 *** 26.66 % ±1.54% ±2.05% ±2.65%
worker/echo.js n=100000 sendsPerBroadcast=10 payload='string' workers=1 *** 32.72 % ±1.60% ±2.11% ±2.73%
worker/messageport.js n=1000000 payload='object' *** 40.28 % ±1.48% ±1.95% ±2.52%
worker/messageport.js n=1000000 payload='string' *** 76.95 % ±2.19% ±2.90% ±3.75%
Also fix handling exceptions returned from `MessagePort::New`.
PR-URL: https://github.com/nodejs/node/pull/31605
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'src/node_messaging.cc')
-rw-r--r-- | src/node_messaging.cc | 63 |
1 files changed, 41 insertions, 22 deletions
diff --git a/src/node_messaging.cc b/src/node_messaging.cc index 99f42c04245..a66cae35d33 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -29,7 +29,6 @@ using v8::Maybe; using v8::MaybeLocal; using v8::Nothing; using v8::Object; -using v8::ObjectTemplate; using v8::SharedArrayBuffer; using v8::String; using v8::Symbol; @@ -170,6 +169,20 @@ uint32_t Message::AddWASMModule(CompiledWasmModule&& mod) { namespace { +MaybeLocal<Function> GetEmitMessageFunction(Local<Context> context) { + Isolate* isolate = context->GetIsolate(); + Local<Object> per_context_bindings; + Local<Value> emit_message_val; + if (!GetPerContextExports(context).ToLocal(&per_context_bindings) || + !per_context_bindings->Get(context, + FIXED_ONE_BYTE_STRING(isolate, "emitMessage")) + .ToLocal(&emit_message_val)) { + return MaybeLocal<Function>(); + } + CHECK(emit_message_val->IsFunction()); + return emit_message_val.As<Function>(); +} + MaybeLocal<Function> GetDOMException(Local<Context> context) { Isolate* isolate = context->GetIsolate(); Local<Object> per_context_bindings; @@ -471,10 +484,14 @@ MessagePort::MessagePort(Environment* env, MessagePort* channel = ContainerOf(&MessagePort::async_, handle); channel->OnMessage(); }; + CHECK_EQ(uv_async_init(env->event_loop(), &async_, onmessage), 0); - async_.data = static_cast<void*>(this); + async_.data = nullptr; // Reset later to indicate success of the constructor. + auto cleanup = OnScopeLeave([&]() { + if (async_.data == nullptr) Close(); + }); Local<Value> fn; if (!wrap->Get(context, env->oninit_symbol()).ToLocal(&fn)) @@ -482,9 +499,16 @@ MessagePort::MessagePort(Environment* env, if (fn->IsFunction()) { Local<Function> init = fn.As<Function>(); - USE(init->Call(context, wrap, 0, nullptr)); + if (init->Call(context, wrap, 0, nullptr).IsEmpty()) + return; } + Local<Function> emit_message_fn; + if (!GetEmitMessageFunction(context).ToLocal(&emit_message_fn)) + return; + emit_message_fn_.Reset(env->isolate(), emit_message_fn); + + async_.data = static_cast<void*>(this); Debug(this, "Created message port"); } @@ -532,6 +556,11 @@ MessagePort* MessagePort::New( return nullptr; MessagePort* port = new MessagePort(env, context, instance); CHECK_NOT_NULL(port); + if (port->IsHandleClosing()) { + // Construction failed with an exception. + return nullptr; + } + if (data) { port->Detach(); port->data_ = std::move(data); @@ -624,16 +653,8 @@ void MessagePort::OnMessage() { continue; } - Local<Object> event; - Local<Value> cb_args[1]; - if (!env()->message_event_object_template()->NewInstance(context) - .ToLocal(&event) || - event->Set(context, env()->data_string(), payload).IsNothing() || - event->Set(context, env()->target_string(), object()).IsNothing() || - (cb_args[0] = event, false) || - MakeCallback(env()->onmessage_string(), - arraysize(cb_args), - cb_args).IsEmpty()) { + Local<Function> emit_message = PersistentToLocal::Strong(emit_message_fn_); + if (MakeCallback(emit_message, 1, &payload).IsEmpty()) { // Re-schedule OnMessage() execution in case of failure. if (data_) TriggerAsync(); @@ -902,6 +923,7 @@ void MessagePort::Entangle(MessagePort* a, MessagePortData* b) { void MessagePort::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("data", data_); + tracker->TrackField("emit_message_fn", emit_message_fn_); } Local<FunctionTemplate> GetMessagePortConstructorTemplate(Environment* env) { @@ -911,8 +933,6 @@ Local<FunctionTemplate> GetMessagePortConstructorTemplate(Environment* env) { if (!templ.IsEmpty()) return templ; - Isolate* isolate = env->isolate(); - { Local<FunctionTemplate> m = env->NewFunctionTemplate(MessagePort::New); m->SetClassName(env->message_port_constructor_string()); @@ -923,13 +943,6 @@ Local<FunctionTemplate> GetMessagePortConstructorTemplate(Environment* env) { env->SetProtoMethod(m, "start", MessagePort::Start); env->set_message_port_constructor_template(m); - - Local<FunctionTemplate> event_ctor = FunctionTemplate::New(isolate); - event_ctor->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "MessageEvent")); - Local<ObjectTemplate> e = event_ctor->InstanceTemplate(); - e->Set(env->data_string(), Null(isolate)); - e->Set(env->target_string(), Null(isolate)); - env->set_message_event_object_template(e); } return GetMessagePortConstructorTemplate(env); @@ -948,7 +961,13 @@ static void MessageChannel(const FunctionCallbackInfo<Value>& args) { Context::Scope context_scope(context); MessagePort* port1 = MessagePort::New(env, context); + if (port1 == nullptr) return; MessagePort* port2 = MessagePort::New(env, context); + if (port2 == nullptr) { + port1->Close(); + return; + } + MessagePort::Entangle(port1, port2); args.This()->Set(context, env->port1_string(), port1->object()) |