Skip to content

Commit

Permalink
src: include Environment in snapshot
Browse files Browse the repository at this point in the history
This snapshots a lot more data for startup than what we did previously
for increased startup performance.

                                                                                       confidence improvement accuracy (*)   (**)  (***)
    misc/startup.js mode='process' script='benchmark/fixtures/require-cachable' dur=1        ***      9.73 %       ±3.78% ±5.03% ±6.54%
    misc/startup.js mode='process' script='test/fixtures/semicolon' dur=1                    ***     36.11 %       ±3.91% ±5.23% ±6.86%
  • Loading branch information
addaleax committed May 8, 2020
1 parent 4bc3d2c commit 8af28fe
Show file tree
Hide file tree
Showing 25 changed files with 1,507 additions and 152 deletions.
1 change: 1 addition & 0 deletions node.gyp
Expand Up @@ -1157,6 +1157,7 @@
'test/cctest/test_per_process.cc',
'test/cctest/test_platform.cc',
'test/cctest/test_json_utils.cc',
'test/cctest/test_snapshot_support.cc',
'test/cctest/test_sockaddr.cc',
'test/cctest/test_traced_value.cc',
'test/cctest/test_util.cc',
Expand Down
48 changes: 43 additions & 5 deletions src/aliased_buffer.h
Expand Up @@ -4,6 +4,8 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <cinttypes>
// TODO(addaleax): There really, really should be an aliased_buffer-inl.h.
#include "snapshot_support-inl.h"
#include "util-inl.h"
#include "v8.h"

Expand All @@ -30,8 +32,10 @@ template <class NativeT,
class V8T,
// SFINAE NativeT to be scalar
typename = std::enable_if_t<std::is_scalar<NativeT>::value>>
class AliasedBufferBase {
class AliasedBufferBase final : public Snapshottable {
public:
AliasedBufferBase() {}

AliasedBufferBase(v8::Isolate* isolate, const size_t count)
: isolate_(isolate), count_(count), byte_offset_(0) {
CHECK_GT(count, 0);
Expand Down Expand Up @@ -243,11 +247,45 @@ class AliasedBufferBase {
count_ = new_capacity;
}

void Serialize(SnapshotCreateData* snapshot_data) const override {
v8::HandleScope handle_scope(isolate_);
snapshot_data->StartWriteEntry("AliasedBuffer");
snapshot_data->WriteUint64(count_);
snapshot_data->WriteUint64(byte_offset_);
v8::Local<V8T> arr = GetJSArray();
snapshot_data->WriteObject(arr->CreationContext(), arr);
snapshot_data->EndWriteEntry();
}

AliasedBufferBase(v8::Local<v8::Context> context,
SnapshotReadData* snapshot_data)
: isolate_(context->GetIsolate()) {
v8::HandleScope handle_scope(isolate_);
uint64_t count, byte_offset;
if (snapshot_data->StartReadEntry("AliasedBuffer").IsNothing() ||
!snapshot_data->ReadUint64().To(&count) ||
!snapshot_data->ReadUint64().To(&byte_offset)) {
return;
}

count_ = count;
byte_offset_ = byte_offset;

v8::Local<V8T> field;
if (!snapshot_data->ReadObject<V8T>(context).To(&field)) return;
js_array_.Reset(isolate_, field);
buffer_ = reinterpret_cast<NativeT*>(static_cast<char*>(
field->Buffer()->GetBackingStore()->Data()) +
byte_offset_);

snapshot_data->EndReadEntry();
}

private:
v8::Isolate* isolate_;
size_t count_;
size_t byte_offset_;
NativeT* buffer_;
v8::Isolate* isolate_ = nullptr;
size_t count_ = 0;
size_t byte_offset_ = 0;
NativeT* buffer_ = nullptr;
v8::Global<V8T> js_array_;
};

Expand Down
10 changes: 9 additions & 1 deletion src/api/environment.cc
Expand Up @@ -5,6 +5,7 @@
#include "node_native_module_env.h"
#include "node_platform.h"
#include "node_v8_platform-inl.h"
#include "snapshot_support-inl.h"
#include "uv.h"

#if HAVE_INSPECTOR
Expand Down Expand Up @@ -390,6 +391,12 @@ Environment* CreateEnvironment(
env->set_abort_on_uncaught_exception(false);
}

if (isolate_data->snapshot_data() != nullptr &&
!isolate_data->snapshot_data()->errors().empty()) {
FreeEnvironment(env);
return nullptr;
}

#if HAVE_INSPECTOR
if (inspector_parent_handle) {
env->InitializeInspector(
Expand All @@ -400,7 +407,8 @@ Environment* CreateEnvironment(
}
#endif

if (env->RunBootstrapping().IsEmpty()) {
if (env->isolate_data()->snapshot_data() == nullptr &&
env->RunBootstrapping().IsEmpty()) {
FreeEnvironment(env);
return nullptr;
}
Expand Down
1 change: 1 addition & 0 deletions src/base_object-inl.h
Expand Up @@ -101,6 +101,7 @@ Environment* BaseObject::env() const {
}

BaseObject* BaseObject::FromJSObject(v8::Local<v8::Value> value) {
DCHECK(value->IsObject());
v8::Local<v8::Object> obj = value.As<v8::Object>();
DCHECK_GE(obj->InternalFieldCount(), BaseObject::kSlot);
return static_cast<BaseObject*>(
Expand Down
8 changes: 7 additions & 1 deletion src/base_object.h
Expand Up @@ -25,6 +25,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "memory_tracker.h"
#include "snapshot_support.h"
#include "v8.h"
#include <type_traits> // std::remove_reference

Expand All @@ -34,7 +35,7 @@ class Environment;
template <typename T, bool kIsWeak>
class BaseObjectPtrImpl;

class BaseObject : public MemoryRetainer {
class BaseObject : public MemoryRetainer, public Snapshottable {
public:
enum InternalFields { kSlot, kInternalFieldCount };

Expand Down Expand Up @@ -101,6 +102,11 @@ class BaseObject : public MemoryRetainer {
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);

static v8::StartupData SerializeInternalFields(
v8::Local<v8::Object> object, int index, void* data);
virtual v8::StartupData SerializeInternalFields(
int index, SnapshotCreateData* snapshot_data) const;

protected:
virtual inline void OnGCCollect();

Expand Down
57 changes: 26 additions & 31 deletions src/env-inl.h
Expand Up @@ -73,33 +73,14 @@ inline worker::Worker* IsolateData::worker_context() const {
return worker_context_;
}

SnapshotReadData* IsolateData::snapshot_data() const {
return snapshot_data_;
}

inline v8::Local<v8::String> IsolateData::async_wrap_provider(int index) const {
return async_wrap_providers_[index].Get(isolate_);
}

inline AsyncHooks::AsyncHooks()
: async_ids_stack_(env()->isolate(), 16 * 2),
fields_(env()->isolate(), kFieldsCount),
async_id_fields_(env()->isolate(), kUidFieldsCount) {
clear_async_id_stack();

// Always perform async_hooks checks, not just when async_hooks is enabled.
// TODO(AndreasMadsen): Consider removing this for LTS releases.
// See discussion in https://github.com/nodejs/node/pull/15454
// When removing this, do it by reverting the commit. Otherwise the test
// and flag changes won't be included.
fields_[kCheck] = 1;

// kDefaultTriggerAsyncId should be -1, this indicates that there is no
// specified default value and it should fallback to the executionAsyncId.
// 0 is not used as the magic value, because that indicates a missing context
// which is different from a default context.
async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1;

// kAsyncIdCounter should start at 1 because that'll be the id the execution
// context during bootstrap (code that runs before entering uv_run()).
async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1;
}
inline AliasedUint32Array& AsyncHooks::fields() {
return fields_;
}
Expand Down Expand Up @@ -128,6 +109,10 @@ inline Environment* AsyncHooks::env() {
return Environment::ForAsyncHooks(this);
}

inline const Environment* AsyncHooks::env() const {
return Environment::ForAsyncHooks(const_cast<AsyncHooks*>(this));
}

// Remember to keep this code aligned with pushAsyncContext() in JS.
inline void AsyncHooks::push_async_context(double async_id,
double trigger_async_id,
Expand Down Expand Up @@ -238,9 +223,6 @@ inline void Environment::PopAsyncCallbackScope() {
async_callback_scope_depth_--;
}

inline ImmediateInfo::ImmediateInfo(v8::Isolate* isolate)
: fields_(isolate, kFieldsCount) {}

inline AliasedUint32Array& ImmediateInfo::fields() {
return fields_;
}
Expand All @@ -265,9 +247,6 @@ inline void ImmediateInfo::ref_count_dec(uint32_t decrement) {
fields_[kRefCount] -= decrement;
}

inline TickInfo::TickInfo(v8::Isolate* isolate)
: fields_(isolate, kFieldsCount) {}

inline AliasedUint8Array& TickInfo::fields() {
return fields_;
}
Expand Down Expand Up @@ -467,15 +446,27 @@ inline void Environment::set_is_in_inspector_console_call(bool value) {
}
#endif

inline const AsyncHooks* Environment::async_hooks() const {
return &async_hooks_;
}

inline AsyncHooks* Environment::async_hooks() {
return &async_hooks_;
}

inline const ImmediateInfo* Environment::immediate_info() const {
return &immediate_info_;
}

inline ImmediateInfo* Environment::immediate_info() {
return &immediate_info_;
}

inline TickInfo* Environment::tick_info() {
inline const TickInfo* Environment::tick_info() const {
return &tick_info_;
}

inline TickInfo* Environment::tick_info() {
return &tick_info_;
}

Expand Down Expand Up @@ -926,6 +917,10 @@ inline performance::PerformanceState* Environment::performance_state() {
return performance_state_.get();
}

const performance::PerformanceState* Environment::performance_state() const {
return performance_state_.get();
}

inline std::unordered_map<std::string, uint64_t>*
Environment::performance_marks() {
return &performance_marks_;
Expand Down Expand Up @@ -1213,7 +1208,7 @@ BaseObject* CleanupHookCallback::GetBaseObject() const {
}

template <typename T>
void Environment::ForEachBaseObject(T&& iterator) {
void Environment::ForEachBaseObject(T&& iterator) const {
for (const auto& hook : cleanup_hooks_) {
BaseObject* obj = hook.GetBaseObject();
if (obj != nullptr)
Expand Down

0 comments on commit 8af28fe

Please sign in to comment.