From d8eef83757c263672832687ac7667927a7d0c059 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Thu, 28 May 2020 00:10:22 -0500 Subject: [PATCH] process: use v8 fast api calls for hrtime Refs: https://github.com/nodejs/node/issues/33374 PR-URL: https://github.com/nodejs/node/pull/33600 Reviewed-By: Anna Henningsen Reviewed-By: Matteo Collina --- lib/internal/process/per_thread.js | 9 +- src/api/environment.cc | 2 + src/node_process_methods.cc | 131 +++++++++++++++++++++------- test/cctest/test_base_object_ptr.cc | 52 ++++++----- 4 files changed, 136 insertions(+), 58 deletions(-) diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index d219cd7298fc15..303e71703d2fe5 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -41,7 +41,6 @@ function assert(x, msg) { function wrapProcessMethods(binding) { const { hrtime: _hrtime, - hrtimeBigInt: _hrtimeBigInt, cpuUsage: _cpuUsage, memoryUsage: _memoryUsage, resourceUsage: _resourceUsage @@ -113,10 +112,10 @@ function wrapProcessMethods(binding) { // The 3 entries filled in by the original process.hrtime contains // the upper/lower 32 bits of the second part of the value, // and the remaining nanoseconds of the value. - const hrValues = new Uint32Array(3); + const hrValues = new Uint32Array(_hrtime.buffer); function hrtime(time) { - _hrtime(hrValues); + _hrtime.hrtime(); if (time !== undefined) { if (!ArrayIsArray(time)) { @@ -140,9 +139,9 @@ function wrapProcessMethods(binding) { // Use a BigUint64Array in the closure because this is actually a bit // faster than simply returning a BigInt from C++ in V8 7.1. - const hrBigintValues = new BigUint64Array(1); + const hrBigintValues = new BigUint64Array(_hrtime.buffer, 0, 1); function hrtimeBigInt() { - _hrtimeBigInt(hrBigintValues); + _hrtime.hrtimeBigInt(); return hrBigintValues[0]; } diff --git a/src/api/environment.cc b/src/api/environment.cc index 5349657d4c5a8d..99b6d8556101fd 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -209,6 +209,8 @@ void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) { // heap based on the actual physical memory. params->constraints.ConfigureDefaults(total_memory, 0); } + params->embedder_wrapper_object_index = BaseObject::InternalFields::kSlot; + params->embedder_wrapper_type_index = std::numeric_limits::max(); } void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) { diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 6013dbb86b72fc..d580f74478cbf7 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -7,6 +7,7 @@ #include "node_process.h" #include "util-inl.h" #include "uv.h" +#include "v8-fast-api-calls.h" #include "v8.h" #include @@ -33,7 +34,7 @@ namespace node { using v8::Array; using v8::ArrayBuffer; -using v8::BigUint64Array; +using v8::BackingStore; using v8::Context; using v8::Float64Array; using v8::FunctionCallbackInfo; @@ -46,7 +47,6 @@ using v8::Number; using v8::Object; using v8::String; using v8::Uint32; -using v8::Uint32Array; using v8::Value; namespace per_process { @@ -131,35 +131,6 @@ static void Cwd(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(cwd); } - -// Hrtime exposes libuv's uv_hrtime() high-resolution timer. - -// This is the legacy version of hrtime before BigInt was introduced in -// JavaScript. -// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds, -// so this function instead fills in an Uint32Array with 3 entries, -// to avoid any integer overflow possibility. -// The first two entries contain the second part of the value -// broken into the upper/lower 32 bits to be converted back in JS, -// because there is no Uint64Array in JS. -// The third entry contains the remaining nanosecond part of the value. -static void Hrtime(const FunctionCallbackInfo& args) { - uint64_t t = uv_hrtime(); - - Local ab = args[0].As()->Buffer(); - uint32_t* fields = static_cast(ab->GetBackingStore()->Data()); - - fields[0] = (t / NANOS_PER_SEC) >> 32; - fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; - fields[2] = t % NANOS_PER_SEC; -} - -static void HrtimeBigInt(const FunctionCallbackInfo& args) { - Local ab = args[0].As()->Buffer(); - uint64_t* fields = static_cast(ab->GetBackingStore()->Data()); - fields[0] = uv_hrtime(); -} - static void Kill(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local context = env->context(); @@ -452,6 +423,85 @@ static void ReallyExit(const FunctionCallbackInfo& args) { env->Exit(code); } +class FastHrtime : public BaseObject { + public: + static Local New(Environment* env) { + Local otmpl = v8::ObjectTemplate::New(env->isolate()); + otmpl->SetInternalFieldCount(FastHrtime::kInternalFieldCount); + + auto create_func = [env](auto fast_func, auto slow_func) { + auto cfunc = v8::CFunction::Make(fast_func); + return v8::FunctionTemplate::New(env->isolate(), + slow_func, + Local(), + Local(), + 0, + v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasNoSideEffect, + &cfunc); + }; + + otmpl->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "hrtime"), + create_func(FastNumber, SlowNumber)); + otmpl->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "hrtimeBigInt"), + create_func(FastBigInt, SlowBigInt)); + + Local obj = otmpl->NewInstance(env->context()).ToLocalChecked(); + + Local ab = ArrayBuffer::New(env->isolate(), 12); + new FastHrtime(env, obj, ab->GetBackingStore()); + obj->Set( + env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "buffer"), ab) + .ToChecked(); + + return obj; + } + + private: + FastHrtime(Environment* env, + Local object, + std::shared_ptr backing_store) + : BaseObject(env, object), backing_store_(backing_store) {} + + void MemoryInfo(MemoryTracker* tracker) const override {} + + SET_MEMORY_INFO_NAME(FastHrtime) + SET_SELF_SIZE(FastHrtime) + + // This is the legacy version of hrtime before BigInt was introduced in + // JavaScript. + // The value returned by uv_hrtime() is a 64-bit int representing nanoseconds, + // so this function instead fills in an Uint32Array with 3 entries, + // to avoid any integer overflow possibility. + // The first two entries contain the second part of the value + // broken into the upper/lower 32 bits to be converted back in JS, + // because there is no Uint64Array in JS. + // The third entry contains the remaining nanosecond part of the value. + static void FastNumber(FastHrtime* receiver) { + uint64_t t = uv_hrtime(); + uint32_t* fields = static_cast(receiver->backing_store_->Data()); + fields[0] = (t / NANOS_PER_SEC) >> 32; + fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; + fields[2] = t % NANOS_PER_SEC; + } + + static void SlowNumber(const FunctionCallbackInfo& args) { + FastNumber(FromJSObject(args.Holder())); + } + + static void FastBigInt(FastHrtime* receiver) { + uint64_t t = uv_hrtime(); + uint64_t* fields = static_cast(receiver->backing_store_->Data()); + fields[0] = t; + } + + static void SlowBigInt(const FunctionCallbackInfo& args) { + FastBigInt(FromJSObject(args.Holder())); + } + + std::shared_ptr backing_store_; +}; + static void InitializeProcessMethods(Local target, Local unused, Local context, @@ -475,8 +525,6 @@ static void InitializeProcessMethods(Local target, env->SetMethod(target, "_rawDebug", RawDebug); env->SetMethod(target, "memoryUsage", MemoryUsage); env->SetMethod(target, "cpuUsage", CPUUsage); - env->SetMethod(target, "hrtime", Hrtime); - env->SetMethod(target, "hrtimeBigInt", HrtimeBigInt); env->SetMethod(target, "resourceUsage", ResourceUsage); env->SetMethod(target, "_getActiveRequests", GetActiveRequests); @@ -488,9 +536,26 @@ static void InitializeProcessMethods(Local target, env->SetMethod(target, "reallyExit", ReallyExit); env->SetMethodNoSideEffect(target, "uptime", Uptime); env->SetMethod(target, "patchProcessObject", PatchProcessObject); + + target + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "hrtime"), + FastHrtime::New(env)) + .ToChecked(); } } // namespace node +namespace v8 { +template <> +class WrapperTraits { + public: + static const void* GetTypeInfo() { + static const int tag = 0; + return reinterpret_cast(&tag); + } +}; +} // namespace v8 + NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_methods, node::InitializeProcessMethods) diff --git a/test/cctest/test_base_object_ptr.cc b/test/cctest/test_base_object_ptr.cc index 18e27edba8cd53..9006ea2a3ba519 100644 --- a/test/cctest/test_base_object_ptr.cc +++ b/test/cctest/test_base_object_ptr.cc @@ -14,6 +14,10 @@ using v8::Isolate; using v8::Local; using v8::Object; +// Environments may come with existing BaseObject instances. +// This variable offsets the expected BaseObject counts. +static const int BASE_OBJECT_COUNT = 1; + class BaseObjectPtrTest : public EnvironmentTestFixture {}; class DummyBaseObject : public BaseObject { @@ -47,12 +51,12 @@ TEST_F(BaseObjectPtrTest, ScopedDetached) { Env env_{handle_scope, argv}; Environment* env = *env_; - EXPECT_EQ(env->base_object_count(), 0); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT); { BaseObjectPtr ptr = DummyBaseObject::NewDetached(env); - EXPECT_EQ(env->base_object_count(), 1); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1); } - EXPECT_EQ(env->base_object_count(), 0); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT); } TEST_F(BaseObjectPtrTest, ScopedDetachedWithWeak) { @@ -63,14 +67,14 @@ TEST_F(BaseObjectPtrTest, ScopedDetachedWithWeak) { BaseObjectWeakPtr weak_ptr; - EXPECT_EQ(env->base_object_count(), 0); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT); { BaseObjectPtr ptr = DummyBaseObject::NewDetached(env); weak_ptr = ptr; - EXPECT_EQ(env->base_object_count(), 1); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1); } EXPECT_EQ(weak_ptr.get(), nullptr); - EXPECT_EQ(env->base_object_count(), 0); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT); } TEST_F(BaseObjectPtrTest, Undetached) { @@ -79,12 +83,16 @@ TEST_F(BaseObjectPtrTest, Undetached) { Env env_{handle_scope, argv}; Environment* env = *env_; - node::AddEnvironmentCleanupHook(isolate_, [](void* arg) { - EXPECT_EQ(static_cast(arg)->base_object_count(), 0); - }, env); + node::AddEnvironmentCleanupHook( + isolate_, + [](void* arg) { + EXPECT_EQ(static_cast(arg)->base_object_count(), + BASE_OBJECT_COUNT); + }, + env); BaseObjectPtr ptr = DummyBaseObject::New(env); - EXPECT_EQ(env->base_object_count(), 1); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1); } TEST_F(BaseObjectPtrTest, GCWeak) { @@ -101,21 +109,21 @@ TEST_F(BaseObjectPtrTest, GCWeak) { weak_ptr = ptr; ptr->MakeWeak(); - EXPECT_EQ(env->base_object_count(), 1); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1); EXPECT_EQ(weak_ptr.get(), ptr.get()); EXPECT_EQ(weak_ptr->persistent().IsWeak(), false); ptr.reset(); } - EXPECT_EQ(env->base_object_count(), 1); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1); EXPECT_NE(weak_ptr.get(), nullptr); EXPECT_EQ(weak_ptr->persistent().IsWeak(), true); v8::V8::SetFlagsFromString("--expose-gc"); isolate_->RequestGarbageCollectionForTesting(Isolate::kFullGarbageCollection); - EXPECT_EQ(env->base_object_count(), 0); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT); EXPECT_EQ(weak_ptr.get(), nullptr); } @@ -126,7 +134,7 @@ TEST_F(BaseObjectPtrTest, Moveable) { Environment* env = *env_; BaseObjectPtr ptr = DummyBaseObject::NewDetached(env); - EXPECT_EQ(env->base_object_count(), 1); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1); BaseObjectWeakPtr weak_ptr { ptr }; EXPECT_EQ(weak_ptr.get(), ptr.get()); @@ -137,12 +145,12 @@ TEST_F(BaseObjectPtrTest, Moveable) { BaseObjectWeakPtr weak_ptr2 = std::move(weak_ptr); EXPECT_EQ(weak_ptr2.get(), ptr2.get()); EXPECT_EQ(weak_ptr.get(), nullptr); - EXPECT_EQ(env->base_object_count(), 1); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 1); ptr2.reset(); EXPECT_EQ(weak_ptr2.get(), nullptr); - EXPECT_EQ(env->base_object_count(), 0); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT); } TEST_F(BaseObjectPtrTest, NestedClasses) { @@ -163,14 +171,18 @@ TEST_F(BaseObjectPtrTest, NestedClasses) { Env env_{handle_scope, argv}; Environment* env = *env_; - node::AddEnvironmentCleanupHook(isolate_, [](void* arg) { - EXPECT_EQ(static_cast(arg)->base_object_count(), 0); - }, env); + node::AddEnvironmentCleanupHook( + isolate_, + [](void* arg) { + EXPECT_EQ(static_cast(arg)->base_object_count(), + BASE_OBJECT_COUNT); + }, + env); ObjectWithPtr* obj = new ObjectWithPtr(env, DummyBaseObject::MakeJSObject(env)); obj->ptr1 = DummyBaseObject::NewDetached(env); obj->ptr2 = DummyBaseObject::New(env); - EXPECT_EQ(env->base_object_count(), 3); + EXPECT_EQ(env->base_object_count(), BASE_OBJECT_COUNT + 3); }