Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6801d37

Browse filesBrowse files
addaleaxMylesBorins
authored andcommitted
src: add snapshot support for embedder API
Add experimental support for loading snapshots in the embedder API by adding a public opaque wrapper for our `SnapshotData` struct and allowing embedders to pass it to the relevant setup functions. Where applicable, use these helpers to deduplicate existing code in Node.js’s startup path. This has shown a 40 % startup performance increase for a real-world application, even with the somewhat limited current support for built-in modules. The documentation includes a note about no guarantees for API or ABI stability for this feature while it is experimental. PR-URL: #45888 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent 95065c3 commit 6801d37
Copy full SHA for 6801d37

File tree

Expand file treeCollapse file tree

15 files changed

+433
-110
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

15 files changed

+433
-110
lines changed
Open diff view settings
Collapse file

‎src/api/embed_helpers.cc‎

Copy file name to clipboardExpand all lines: src/api/embed_helpers.cc
+60-5Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
#include "node.h"
2-
#include "env-inl.h"
31
#include "debug_utils-inl.h"
2+
#include "env-inl.h"
3+
#include "node.h"
4+
#include "node_snapshot_builder.h"
45

56
using v8::Context;
67
using v8::Function;
@@ -86,8 +87,9 @@ struct CommonEnvironmentSetup::Impl {
8687
CommonEnvironmentSetup::CommonEnvironmentSetup(
8788
MultiIsolatePlatform* platform,
8889
std::vector<std::string>* errors,
90+
const EmbedderSnapshotData* snapshot_data,
8991
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
90-
: impl_(new Impl()) {
92+
: impl_(new Impl()) {
9193
CHECK_NOT_NULL(platform);
9294
CHECK_NOT_NULL(errors);
9395

@@ -104,16 +106,25 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
104106
loop->data = this;
105107

106108
impl_->allocator = ArrayBufferAllocator::Create();
107-
impl_->isolate = NewIsolate(impl_->allocator, &impl_->loop, platform);
109+
impl_->isolate =
110+
NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data);
108111
Isolate* isolate = impl_->isolate;
109112

110113
{
111114
Locker locker(isolate);
112115
Isolate::Scope isolate_scope(isolate);
113116
impl_->isolate_data.reset(CreateIsolateData(
114-
isolate, loop, platform, impl_->allocator.get()));
117+
isolate, loop, platform, impl_->allocator.get(), snapshot_data));
115118

116119
HandleScope handle_scope(isolate);
120+
if (snapshot_data) {
121+
impl_->env.reset(make_env(this));
122+
if (impl_->env) {
123+
impl_->context.Reset(isolate, impl_->env->context());
124+
}
125+
return;
126+
}
127+
117128
Local<Context> context = NewContext(isolate);
118129
impl_->context.Reset(isolate, context);
119130
if (context.IsEmpty()) {
@@ -126,6 +137,12 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
126137
}
127138
}
128139

140+
CommonEnvironmentSetup::CommonEnvironmentSetup(
141+
MultiIsolatePlatform* platform,
142+
std::vector<std::string>* errors,
143+
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
144+
: CommonEnvironmentSetup(platform, errors, nullptr, make_env) {}
145+
129146
CommonEnvironmentSetup::~CommonEnvironmentSetup() {
130147
if (impl_->isolate != nullptr) {
131148
Isolate* isolate = impl_->isolate;
@@ -189,4 +206,42 @@ v8::Local<v8::Context> CommonEnvironmentSetup::context() const {
189206
return impl_->context.Get(impl_->isolate);
190207
}
191208

209+
void EmbedderSnapshotData::DeleteSnapshotData::operator()(
210+
const EmbedderSnapshotData* data) const {
211+
CHECK_IMPLIES(data->owns_impl_, data->impl_);
212+
if (data->owns_impl_ &&
213+
data->impl_->data_ownership == SnapshotData::DataOwnership::kOwned) {
214+
delete data->impl_;
215+
}
216+
delete data;
217+
}
218+
219+
EmbedderSnapshotData::Pointer EmbedderSnapshotData::BuiltinSnapshotData() {
220+
return EmbedderSnapshotData::Pointer{new EmbedderSnapshotData(
221+
SnapshotBuilder::GetEmbeddedSnapshotData(), false)};
222+
}
223+
224+
EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) {
225+
SnapshotData* snapshot_data = new SnapshotData();
226+
CHECK_EQ(snapshot_data->data_ownership, SnapshotData::DataOwnership::kOwned);
227+
EmbedderSnapshotData::Pointer result{
228+
new EmbedderSnapshotData(snapshot_data, true)};
229+
if (!SnapshotData::FromBlob(snapshot_data, in)) {
230+
return {};
231+
}
232+
return result;
233+
}
234+
235+
EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl,
236+
bool owns_impl)
237+
: impl_(impl), owns_impl_(owns_impl) {}
238+
239+
bool EmbedderSnapshotData::CanUseCustomSnapshotPerIsolate() {
240+
#ifdef NODE_V8_SHARED_RO_HEAP
241+
return false;
242+
#else
243+
return true;
244+
#endif
245+
}
246+
192247
} // namespace node
Collapse file

‎src/api/environment.cc‎

Copy file name to clipboardExpand all lines: src/api/environment.cc
+92-14Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "node_platform.h"
1010
#include "node_realm-inl.h"
1111
#include "node_shadow_realm.h"
12+
#include "node_snapshot_builder.h"
1213
#include "node_v8_platform-inl.h"
1314
#include "node_wasm_web_api.h"
1415
#include "uv.h"
@@ -315,9 +316,15 @@ void SetIsolateUpForNode(v8::Isolate* isolate) {
315316
Isolate* NewIsolate(Isolate::CreateParams* params,
316317
uv_loop_t* event_loop,
317318
MultiIsolatePlatform* platform,
318-
bool has_snapshot_data) {
319+
const SnapshotData* snapshot_data,
320+
const IsolateSettings& settings) {
319321
Isolate* isolate = Isolate::Allocate();
320322
if (isolate == nullptr) return nullptr;
323+
324+
if (snapshot_data != nullptr) {
325+
SnapshotBuilder::InitializeIsolateParams(snapshot_data, params);
326+
}
327+
321328
#ifdef NODE_V8_SHARED_RO_HEAP
322329
{
323330
// In shared-readonly-heap mode, V8 requires all snapshots used for
@@ -336,38 +343,73 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
336343

337344
SetIsolateCreateParamsForNode(params);
338345
Isolate::Initialize(isolate, *params);
339-
if (!has_snapshot_data) {
346+
if (snapshot_data == nullptr) {
340347
// If in deserialize mode, delay until after the deserialization is
341348
// complete.
342-
SetIsolateUpForNode(isolate);
349+
SetIsolateUpForNode(isolate, settings);
343350
} else {
344-
SetIsolateMiscHandlers(isolate, {});
351+
SetIsolateMiscHandlers(isolate, settings);
345352
}
346353

347354
return isolate;
348355
}
349356

350357
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
351358
uv_loop_t* event_loop,
352-
MultiIsolatePlatform* platform) {
359+
MultiIsolatePlatform* platform,
360+
const EmbedderSnapshotData* snapshot_data,
361+
const IsolateSettings& settings) {
353362
Isolate::CreateParams params;
354363
if (allocator != nullptr) params.array_buffer_allocator = allocator;
355-
return NewIsolate(&params, event_loop, platform);
364+
return NewIsolate(&params,
365+
event_loop,
366+
platform,
367+
SnapshotData::FromEmbedderWrapper(snapshot_data),
368+
settings);
356369
}
357370

358371
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
359372
uv_loop_t* event_loop,
360-
MultiIsolatePlatform* platform) {
373+
MultiIsolatePlatform* platform,
374+
const EmbedderSnapshotData* snapshot_data,
375+
const IsolateSettings& settings) {
361376
Isolate::CreateParams params;
362377
if (allocator) params.array_buffer_allocator_shared = allocator;
363-
return NewIsolate(&params, event_loop, platform);
378+
return NewIsolate(&params,
379+
event_loop,
380+
platform,
381+
SnapshotData::FromEmbedderWrapper(snapshot_data),
382+
settings);
383+
}
384+
385+
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
386+
uv_loop_t* event_loop,
387+
MultiIsolatePlatform* platform) {
388+
return NewIsolate(allocator, event_loop, platform, nullptr);
389+
}
390+
391+
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
392+
uv_loop_t* event_loop,
393+
MultiIsolatePlatform* platform) {
394+
return NewIsolate(allocator, event_loop, platform, nullptr);
395+
}
396+
397+
IsolateData* CreateIsolateData(
398+
Isolate* isolate,
399+
uv_loop_t* loop,
400+
MultiIsolatePlatform* platform,
401+
ArrayBufferAllocator* allocator,
402+
const EmbedderSnapshotData* embedder_snapshot_data) {
403+
const SnapshotData* snapshot_data =
404+
SnapshotData::FromEmbedderWrapper(embedder_snapshot_data);
405+
return new IsolateData(isolate, loop, platform, allocator, snapshot_data);
364406
}
365407

366408
IsolateData* CreateIsolateData(Isolate* isolate,
367409
uv_loop_t* loop,
368410
MultiIsolatePlatform* platform,
369411
ArrayBufferAllocator* allocator) {
370-
return new IsolateData(isolate, loop, platform, allocator);
412+
return CreateIsolateData(isolate, loop, platform, allocator, nullptr);
371413
}
372414

373415
void FreeIsolateData(IsolateData* isolate_data) {
@@ -395,13 +437,45 @@ Environment* CreateEnvironment(
395437
EnvironmentFlags::Flags flags,
396438
ThreadId thread_id,
397439
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
398-
Isolate* isolate = context->GetIsolate();
440+
Isolate* isolate = isolate_data->isolate();
399441
HandleScope handle_scope(isolate);
400-
Context::Scope context_scope(context);
442+
443+
const bool use_snapshot = context.IsEmpty();
444+
const EnvSerializeInfo* env_snapshot_info = nullptr;
445+
if (use_snapshot) {
446+
CHECK_NOT_NULL(isolate_data->snapshot_data());
447+
env_snapshot_info = &isolate_data->snapshot_data()->env_info;
448+
}
449+
401450
// TODO(addaleax): This is a much better place for parsing per-Environment
402451
// options than the global parse call.
403-
Environment* env = new Environment(
404-
isolate_data, context, args, exec_args, nullptr, flags, thread_id);
452+
Environment* env = new Environment(isolate_data,
453+
isolate,
454+
args,
455+
exec_args,
456+
env_snapshot_info,
457+
flags,
458+
thread_id);
459+
CHECK_NOT_NULL(env);
460+
461+
if (use_snapshot) {
462+
context = Context::FromSnapshot(isolate,
463+
SnapshotData::kNodeMainContextIndex,
464+
{DeserializeNodeInternalFields, env})
465+
.ToLocalChecked();
466+
467+
CHECK(!context.IsEmpty());
468+
Context::Scope context_scope(context);
469+
470+
if (InitializeContextRuntime(context).IsNothing()) {
471+
FreeEnvironment(env);
472+
return nullptr;
473+
}
474+
SetIsolateErrorHandlers(isolate, {});
475+
}
476+
477+
Context::Scope context_scope(context);
478+
env->InitializeMainContext(context, env_snapshot_info);
405479

406480
#if HAVE_INSPECTOR
407481
if (env->should_create_inspector()) {
@@ -415,7 +489,7 @@ Environment* CreateEnvironment(
415489
}
416490
#endif
417491

418-
if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
492+
if (!use_snapshot && env->principal_realm()->RunBootstrapping().IsEmpty()) {
419493
FreeEnvironment(env);
420494
return nullptr;
421495
}
@@ -500,6 +574,10 @@ ArrayBufferAllocator* GetArrayBufferAllocator(IsolateData* isolate_data) {
500574
return isolate_data->node_allocator();
501575
}
502576

577+
Local<Context> GetMainContext(Environment* env) {
578+
return env->context();
579+
}
580+
503581
MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env) {
504582
return GetMultiIsolatePlatform(env->isolate_data());
505583
}
Collapse file

‎src/env-inl.h‎

Copy file name to clipboardExpand all lines: src/env-inl.h
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ inline MultiIsolatePlatform* IsolateData::platform() const {
6969
return platform_;
7070
}
7171

72+
inline const SnapshotData* IsolateData::snapshot_data() const {
73+
return snapshot_data_;
74+
}
75+
7276
inline void IsolateData::set_worker_context(worker::Worker* context) {
7377
CHECK_NULL(worker_context_); // Should be set only once.
7478
worker_context_ = context;
Collapse file

‎src/env.cc‎

Copy file name to clipboardExpand all lines: src/env.cc
+17-24Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -472,19 +472,20 @@ IsolateData::IsolateData(Isolate* isolate,
472472
uv_loop_t* event_loop,
473473
MultiIsolatePlatform* platform,
474474
ArrayBufferAllocator* node_allocator,
475-
const IsolateDataSerializeInfo* isolate_data_info)
475+
const SnapshotData* snapshot_data)
476476
: isolate_(isolate),
477477
event_loop_(event_loop),
478478
node_allocator_(node_allocator == nullptr ? nullptr
479479
: node_allocator->GetImpl()),
480-
platform_(platform) {
480+
platform_(platform),
481+
snapshot_data_(snapshot_data) {
481482
options_.reset(
482483
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
483484

484-
if (isolate_data_info == nullptr) {
485+
if (snapshot_data == nullptr) {
485486
CreateProperties();
486487
} else {
487-
DeserializeProperties(isolate_data_info);
488+
DeserializeProperties(&snapshot_data->isolate_data_info);
488489
}
489490
}
490491

@@ -674,14 +675,23 @@ Environment::Environment(IsolateData* isolate_data,
674675
thread_id_(thread_id.id == static_cast<uint64_t>(-1)
675676
? AllocateEnvironmentThreadId().id
676677
: thread_id.id) {
678+
constexpr bool is_shared_ro_heap =
677679
#ifdef NODE_V8_SHARED_RO_HEAP
678-
if (!is_main_thread()) {
680+
true;
681+
#else
682+
false;
683+
#endif
684+
if (is_shared_ro_heap && !is_main_thread()) {
685+
// If this is a Worker thread and we are in shared-readonly-heap mode,
686+
// we can always safely use the parent's Isolate's code cache.
679687
CHECK_NOT_NULL(isolate_data->worker_context());
680-
// TODO(addaleax): Adjust for the embedder API snapshot support changes
681688
builtin_loader()->CopySourceAndCodeCacheReferenceFrom(
682689
isolate_data->worker_context()->env()->builtin_loader());
690+
} else if (isolate_data->snapshot_data() != nullptr) {
691+
// ... otherwise, if a snapshot was provided, use its code cache.
692+
builtin_loader()->RefreshCodeCache(
693+
isolate_data->snapshot_data()->code_cache);
683694
}
684-
#endif
685695

686696
// We'll be creating new objects so make sure we've entered the context.
687697
HandleScope handle_scope(isolate);
@@ -746,23 +756,6 @@ Environment::Environment(IsolateData* isolate_data,
746756
}
747757
}
748758

749-
Environment::Environment(IsolateData* isolate_data,
750-
Local<Context> context,
751-
const std::vector<std::string>& args,
752-
const std::vector<std::string>& exec_args,
753-
const EnvSerializeInfo* env_info,
754-
EnvironmentFlags::Flags flags,
755-
ThreadId thread_id)
756-
: Environment(isolate_data,
757-
context->GetIsolate(),
758-
args,
759-
exec_args,
760-
env_info,
761-
flags,
762-
thread_id) {
763-
InitializeMainContext(context, env_info);
764-
}
765-
766759
void Environment::InitializeMainContext(Local<Context> context,
767760
const EnvSerializeInfo* env_info) {
768761
principal_realm_ = std::make_unique<Realm>(

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.