From 00227674f5e68f12059c21ddeab129dfabc64f5e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 29 Dec 2023 08:40:34 -0800 Subject: [PATCH] src: add fast api for Histogram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/51296 Reviewed-By: Yagiz Nizipli Reviewed-By: Vinícius Lourenço Claro Cardoso Reviewed-By: Mohammed Keyvanzadeh Reviewed-By: Benjamin Gruenbaum Reviewed-By: Minwoo Jung --- benchmark/perf_hooks/histogram-record.js | 26 ++++ src/histogram.cc | 159 +++++++++++++++++++++-- src/histogram.h | 35 +++++ src/node_external_reference.h | 9 ++ 4 files changed, 215 insertions(+), 14 deletions(-) create mode 100644 benchmark/perf_hooks/histogram-record.js diff --git a/benchmark/perf_hooks/histogram-record.js b/benchmark/perf_hooks/histogram-record.js new file mode 100644 index 00000000000000..c3f7e89e171b1b --- /dev/null +++ b/benchmark/perf_hooks/histogram-record.js @@ -0,0 +1,26 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common.js'); + +const { createHistogram } = require('perf_hooks'); + +const bench = common.createBenchmark(main, { + n: [1e5], +}); + +function main({ n }) { + const histogram = createHistogram(); + bench.start(); + for (let i = 0; i < n; i++) { + histogram.record(i + 1); + /* eslint-disable no-unused-expressions */ + histogram.max; + histogram.mean; + /* eslint-enable no-unused-expressions */ + } + bench.end(n); + + // Avoid V8 deadcode (elimination) + assert.ok(histogram); +} diff --git a/src/histogram.cc b/src/histogram.cc index ba506966c2d36b..4f58359fe58529 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -9,7 +9,9 @@ namespace node { using v8::BigInt; +using v8::CFunction; using v8::Context; +using v8::FastApiCallbackOptions; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Integer; @@ -42,7 +44,34 @@ HistogramImpl::HistogramImpl(const Histogram::Options& options) HistogramImpl::HistogramImpl(std::shared_ptr histogram) : histogram_(std::move(histogram)) {} +CFunction HistogramImpl::fast_reset_( + CFunction::Make(&HistogramImpl::FastReset)); +CFunction HistogramImpl::fast_get_count_( + CFunction::Make(&HistogramImpl::FastGetCount)); +CFunction HistogramImpl::fast_get_min_( + CFunction::Make(&HistogramImpl::FastGetMin)); +CFunction HistogramImpl::fast_get_max_( + CFunction::Make(&HistogramImpl::FastGetMax)); +CFunction HistogramImpl::fast_get_mean_( + CFunction::Make(&HistogramImpl::FastGetMean)); +CFunction HistogramImpl::fast_get_exceeds_( + CFunction::Make(&HistogramImpl::FastGetExceeds)); +CFunction HistogramImpl::fast_get_stddev_( + CFunction::Make(&HistogramImpl::FastGetStddev)); +CFunction HistogramImpl::fast_get_percentile_( + CFunction::Make(&HistogramImpl::FastGetPercentile)); +CFunction HistogramBase::fast_record_( + CFunction::Make(&HistogramBase::FastRecord)); +CFunction HistogramBase::fast_record_delta_( + CFunction::Make(&HistogramBase::FastRecordDelta)); +CFunction IntervalHistogram::fast_start_( + CFunction::Make(&IntervalHistogram::FastStart)); +CFunction IntervalHistogram::fast_stop_( + CFunction::Make(&IntervalHistogram::FastStop)); + void HistogramImpl::AddMethods(Isolate* isolate, Local tmpl) { + // TODO(@jasnell): The bigint API variations do not yet support fast + // variations since v8 will not return a bigint value from a fast method. SetProtoMethodNoSideEffect(isolate, tmpl, "countBigInt", GetCountBigInt); SetProtoMethodNoSideEffect(isolate, tmpl, "exceedsBigInt", GetExceedsBigInt); SetProtoMethodNoSideEffect(isolate, tmpl, "minBigInt", GetMinBigInt); @@ -52,14 +81,20 @@ void HistogramImpl::AddMethods(Isolate* isolate, Local tmpl) { SetProtoMethodNoSideEffect(isolate, tmpl, "percentiles", GetPercentiles); SetProtoMethodNoSideEffect( isolate, tmpl, "percentilesBigInt", GetPercentilesBigInt); - SetProtoMethodNoSideEffect(isolate, tmpl, "count", GetCount); - SetProtoMethodNoSideEffect(isolate, tmpl, "exceeds", GetExceeds); - SetProtoMethodNoSideEffect(isolate, tmpl, "min", GetMin); - SetProtoMethodNoSideEffect(isolate, tmpl, "max", GetMax); - SetProtoMethodNoSideEffect(isolate, tmpl, "mean", GetMean); - SetProtoMethodNoSideEffect(isolate, tmpl, "stddev", GetStddev); - SetProtoMethodNoSideEffect(isolate, tmpl, "percentile", GetPercentile); - SetProtoMethod(isolate, tmpl, "reset", DoReset); + auto instance = tmpl->InstanceTemplate(); + SetFastMethodNoSideEffect( + isolate, instance, "count", GetCount, &fast_get_count_); + SetFastMethodNoSideEffect( + isolate, instance, "exceeds", GetExceeds, &fast_get_exceeds_); + SetFastMethodNoSideEffect(isolate, instance, "min", GetMin, &fast_get_min_); + SetFastMethodNoSideEffect(isolate, instance, "max", GetMax, &fast_get_max_); + SetFastMethodNoSideEffect( + isolate, instance, "mean", GetMean, &fast_get_mean_); + SetFastMethodNoSideEffect( + isolate, instance, "stddev", GetStddev, &fast_get_stddev_); + SetFastMethodNoSideEffect( + isolate, instance, "percentile", GetPercentile, &fast_get_percentile_); + SetFastMethod(isolate, instance, "reset", DoReset, &fast_reset_); } void HistogramImpl::RegisterExternalReferences( @@ -81,6 +116,22 @@ void HistogramImpl::RegisterExternalReferences( registry->Register(GetPercentiles); registry->Register(GetPercentilesBigInt); registry->Register(DoReset); + registry->Register(fast_reset_.GetTypeInfo()); + registry->Register(fast_get_count_.GetTypeInfo()); + registry->Register(fast_get_min_.GetTypeInfo()); + registry->Register(fast_get_max_.GetTypeInfo()); + registry->Register(fast_get_mean_.GetTypeInfo()); + registry->Register(fast_get_exceeds_.GetTypeInfo()); + registry->Register(fast_get_stddev_.GetTypeInfo()); + registry->Register(fast_get_percentile_.GetTypeInfo()); + registry->Register(FastReset); + registry->Register(FastGetCount); + registry->Register(FastGetMin); + registry->Register(FastGetMax); + registry->Register(FastGetMean); + registry->Register(FastGetExceeds); + registry->Register(FastGetStddev); + registry->Register(FastGetPercentile); is_registerd = true; } @@ -118,6 +169,12 @@ void HistogramBase::RecordDelta(const FunctionCallbackInfo& args) { (*histogram)->RecordDelta(); } +void HistogramBase::FastRecordDelta(Local receiver) { + HistogramBase* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); + (*histogram)->RecordDelta(); +} + void HistogramBase::Record(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK_IMPLIES(!args[0]->IsNumber(), args[0]->IsBigInt()); @@ -132,6 +189,18 @@ void HistogramBase::Record(const FunctionCallbackInfo& args) { (*histogram)->Record(value); } +void HistogramBase::FastRecord(Local receiver, + const int64_t value, + FastApiCallbackOptions& options) { + if (value < 1) { + options.fallback = true; + return; + } + HistogramBase* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); + (*histogram)->Record(value); +} + void HistogramBase::Add(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); HistogramBase* histogram; @@ -213,8 +282,9 @@ Local HistogramBase::GetConstructorTemplate( tmpl->SetClassName(classname); auto instance = tmpl->InstanceTemplate(); instance->SetInternalFieldCount(HistogramImpl::kInternalFieldCount); - SetProtoMethod(isolate, tmpl, "record", Record); - SetProtoMethod(isolate, tmpl, "recordDelta", RecordDelta); + SetFastMethod(isolate, instance, "record", Record, &fast_record_); + SetFastMethod( + isolate, instance, "recordDelta", RecordDelta, &fast_record_delta_); SetProtoMethod(isolate, tmpl, "add", Add); HistogramImpl::AddMethods(isolate, tmpl); isolate_data->set_histogram_ctor_template(tmpl); @@ -228,6 +298,10 @@ void HistogramBase::RegisterExternalReferences( registry->Register(Add); registry->Register(Record); registry->Register(RecordDelta); + registry->Register(fast_record_.GetTypeInfo()); + registry->Register(fast_record_delta_.GetTypeInfo()); + registry->Register(FastRecord); + registry->Register(FastRecordDelta); HistogramImpl::RegisterExternalReferences(registry); } @@ -264,11 +338,11 @@ Local IntervalHistogram::GetConstructorTemplate( tmpl = NewFunctionTemplate(isolate, nullptr); tmpl->Inherit(HandleWrap::GetConstructorTemplate(env)); tmpl->SetClassName(OneByteString(isolate, "Histogram")); - tmpl->InstanceTemplate()->SetInternalFieldCount( - HistogramImpl::kInternalFieldCount); + auto instance = tmpl->InstanceTemplate(); + instance->SetInternalFieldCount(HistogramImpl::kInternalFieldCount); HistogramImpl::AddMethods(isolate, tmpl); - SetProtoMethod(isolate, tmpl, "start", Start); - SetProtoMethod(isolate, tmpl, "stop", Stop); + SetFastMethod(isolate, instance, "start", Start, &fast_start_); + SetFastMethod(isolate, instance, "stop", Stop, &fast_stop_); env->set_intervalhistogram_constructor_template(tmpl); } return tmpl; @@ -278,6 +352,10 @@ void IntervalHistogram::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(Start); registry->Register(Stop); + registry->Register(fast_start_.GetTypeInfo()); + registry->Register(fast_stop_.GetTypeInfo()); + registry->Register(FastStart); + registry->Register(FastStop); HistogramImpl::RegisterExternalReferences(registry); } @@ -358,12 +436,24 @@ void IntervalHistogram::Start(const FunctionCallbackInfo& args) { histogram->OnStart(args[0]->IsTrue() ? StartFlags::RESET : StartFlags::NONE); } +void IntervalHistogram::FastStart(Local receiver, bool reset) { + IntervalHistogram* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); + histogram->OnStart(reset ? StartFlags::RESET : StartFlags::NONE); +} + void IntervalHistogram::Stop(const FunctionCallbackInfo& args) { IntervalHistogram* histogram; ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder()); histogram->OnStop(); } +void IntervalHistogram::FastStop(Local receiver) { + IntervalHistogram* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); + histogram->OnStop(); +} + void HistogramImpl::GetCount(const FunctionCallbackInfo& args) { HistogramImpl* histogram = HistogramImpl::FromJSObject(args.Holder()); double value = static_cast((*histogram)->Count()); @@ -474,6 +564,47 @@ void HistogramImpl::DoReset(const FunctionCallbackInfo& args) { (*histogram)->Reset(); } +void HistogramImpl::FastReset(Local receiver) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + (*histogram)->Reset(); +} + +double HistogramImpl::FastGetCount(Local receiver) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + return static_cast((*histogram)->Count()); +} + +double HistogramImpl::FastGetMin(Local receiver) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + return static_cast((*histogram)->Min()); +} + +double HistogramImpl::FastGetMax(Local receiver) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + return static_cast((*histogram)->Max()); +} + +double HistogramImpl::FastGetMean(Local receiver) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + return (*histogram)->Mean(); +} + +double HistogramImpl::FastGetExceeds(Local receiver) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + return static_cast((*histogram)->Exceeds()); +} + +double HistogramImpl::FastGetStddev(Local receiver) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + return (*histogram)->Stddev(); +} + +double HistogramImpl::FastGetPercentile(Local receiver, + const double percentile) { + HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver); + return static_cast((*histogram)->Percentile(percentile)); +} + HistogramImpl* HistogramImpl::FromJSObject(Local value) { auto obj = value.As(); DCHECK_GE(obj->InternalFieldCount(), HistogramImpl::kInternalFieldCount); diff --git a/src/histogram.h b/src/histogram.h index 9378d9ab207bca..65e2ec0db6a68d 100644 --- a/src/histogram.h +++ b/src/histogram.h @@ -101,6 +101,16 @@ class HistogramImpl { static void GetPercentilesBigInt( const v8::FunctionCallbackInfo& args); + static void FastReset(v8::Local receiver); + static double FastGetCount(v8::Local receiver); + static double FastGetMin(v8::Local receiver); + static double FastGetMax(v8::Local receiver); + static double FastGetMean(v8::Local receiver); + static double FastGetExceeds(v8::Local receiver); + static double FastGetStddev(v8::Local receiver); + static double FastGetPercentile(v8::Local receiver, + const double percentile); + static void AddMethods(v8::Isolate* isolate, v8::Local tmpl); @@ -110,6 +120,15 @@ class HistogramImpl { private: std::shared_ptr histogram_; + + static v8::CFunction fast_reset_; + static v8::CFunction fast_get_count_; + static v8::CFunction fast_get_min_; + static v8::CFunction fast_get_max_; + static v8::CFunction fast_get_mean_; + static v8::CFunction fast_get_exceeds_; + static v8::CFunction fast_get_stddev_; + static v8::CFunction fast_get_percentile_; }; class HistogramBase final : public BaseObject, public HistogramImpl { @@ -138,6 +157,12 @@ class HistogramBase final : public BaseObject, public HistogramImpl { static void RecordDelta(const v8::FunctionCallbackInfo& args); static void Add(const v8::FunctionCallbackInfo& args); + static void FastRecord( + v8::Local receiver, + const int64_t value, + v8::FastApiCallbackOptions& options); // NOLINT(runtime/references) + static void FastRecordDelta(v8::Local receiver); + HistogramBase( Environment* env, v8::Local wrap, @@ -173,6 +198,10 @@ class HistogramBase final : public BaseObject, public HistogramImpl { private: std::shared_ptr histogram_; }; + + private: + static v8::CFunction fast_record_; + static v8::CFunction fast_record_delta_; }; class IntervalHistogram final : public HandleWrap, public HistogramImpl { @@ -204,6 +233,9 @@ class IntervalHistogram final : public HandleWrap, public HistogramImpl { static void Start(const v8::FunctionCallbackInfo& args); static void Stop(const v8::FunctionCallbackInfo& args); + static void FastStart(v8::Local receiver, bool reset); + static void FastStop(v8::Local receiver); + TransferMode GetTransferMode() const override { return TransferMode::kCloneable; } @@ -222,6 +254,9 @@ class IntervalHistogram final : public HandleWrap, public HistogramImpl { int32_t interval_ = 0; std::function on_interval_; uv_timer_t timer_; + + static v8::CFunction fast_start_; + static v8::CFunction fast_stop_; }; } // namespace node diff --git a/src/node_external_reference.h b/src/node_external_reference.h index 35e1278c74b83e..2e258a91aac5e3 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -29,6 +29,12 @@ using CFunctionCallbackWithStrings = const v8::FastOneByteString& base); using CFunctionWithUint32 = uint32_t (*)(v8::Local, const uint32_t input); +using CFunctionWithDoubleReturnDouble = double (*)(v8::Local, + const double); +using CFunctionWithInt64Fallback = void (*)(v8::Local, + const int64_t, + v8::FastApiCallbackOptions&); +using CFunctionWithBool = void (*)(v8::Local, bool); // This class manages the external references from the V8 heap // to the C++ addresses in Node.js. @@ -46,6 +52,9 @@ class ExternalReferenceRegistry { V(CFunctionCallbackWithString) \ V(CFunctionCallbackWithStrings) \ V(CFunctionWithUint32) \ + V(CFunctionWithDoubleReturnDouble) \ + V(CFunctionWithInt64Fallback) \ + V(CFunctionWithBool) \ V(const v8::CFunctionInfo*) \ V(v8::FunctionCallback) \ V(v8::AccessorGetterCallback) \