Skip to content

Commit

Permalink
src: add fast api for Histogram
Browse files Browse the repository at this point in the history
PR-URL: #51296
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
  • Loading branch information
jasnell authored and richardlau committed Mar 25, 2024
1 parent 3b3a297 commit 5140cd3
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 14 deletions.
26 changes: 26 additions & 0 deletions benchmark/perf_hooks/histogram-record.js
Original file line number Diff line number Diff line change
@@ -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);
}
159 changes: 145 additions & 14 deletions src/histogram.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -42,7 +44,34 @@ HistogramImpl::HistogramImpl(const Histogram::Options& options)
HistogramImpl::HistogramImpl(std::shared_ptr<Histogram> 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<FunctionTemplate> 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);
Expand All @@ -52,14 +81,20 @@ void HistogramImpl::AddMethods(Isolate* isolate, Local<FunctionTemplate> 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(
Expand All @@ -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;
}

Expand Down Expand Up @@ -118,6 +169,12 @@ void HistogramBase::RecordDelta(const FunctionCallbackInfo<Value>& args) {
(*histogram)->RecordDelta();
}

void HistogramBase::FastRecordDelta(Local<Value> receiver) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver);
(*histogram)->RecordDelta();
}

void HistogramBase::Record(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK_IMPLIES(!args[0]->IsNumber(), args[0]->IsBigInt());
Expand All @@ -132,6 +189,18 @@ void HistogramBase::Record(const FunctionCallbackInfo<Value>& args) {
(*histogram)->Record(value);
}

void HistogramBase::FastRecord(Local<Value> 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<Value>& args) {
Environment* env = Environment::GetCurrent(args);
HistogramBase* histogram;
Expand Down Expand Up @@ -213,8 +282,9 @@ Local<FunctionTemplate> 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);
Expand All @@ -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);
}

Expand Down Expand Up @@ -264,11 +338,11 @@ Local<FunctionTemplate> 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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -358,12 +436,24 @@ void IntervalHistogram::Start(const FunctionCallbackInfo<Value>& args) {
histogram->OnStart(args[0]->IsTrue() ? StartFlags::RESET : StartFlags::NONE);
}

void IntervalHistogram::FastStart(Local<Value> receiver, bool reset) {
IntervalHistogram* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver);
histogram->OnStart(reset ? StartFlags::RESET : StartFlags::NONE);
}

void IntervalHistogram::Stop(const FunctionCallbackInfo<Value>& args) {
IntervalHistogram* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
histogram->OnStop();
}

void IntervalHistogram::FastStop(Local<Value> receiver) {
IntervalHistogram* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver);
histogram->OnStop();
}

void HistogramImpl::GetCount(const FunctionCallbackInfo<Value>& args) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(args.Holder());
double value = static_cast<double>((*histogram)->Count());
Expand Down Expand Up @@ -474,6 +564,47 @@ void HistogramImpl::DoReset(const FunctionCallbackInfo<Value>& args) {
(*histogram)->Reset();
}

void HistogramImpl::FastReset(Local<Value> receiver) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
(*histogram)->Reset();
}

double HistogramImpl::FastGetCount(Local<Value> receiver) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
return static_cast<double>((*histogram)->Count());
}

double HistogramImpl::FastGetMin(Local<Value> receiver) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
return static_cast<double>((*histogram)->Min());
}

double HistogramImpl::FastGetMax(Local<Value> receiver) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
return static_cast<double>((*histogram)->Max());
}

double HistogramImpl::FastGetMean(Local<Value> receiver) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
return (*histogram)->Mean();
}

double HistogramImpl::FastGetExceeds(Local<Value> receiver) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
return static_cast<double>((*histogram)->Exceeds());
}

double HistogramImpl::FastGetStddev(Local<Value> receiver) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
return (*histogram)->Stddev();
}

double HistogramImpl::FastGetPercentile(Local<Value> receiver,
const double percentile) {
HistogramImpl* histogram = HistogramImpl::FromJSObject(receiver);
return static_cast<double>((*histogram)->Percentile(percentile));
}

HistogramImpl* HistogramImpl::FromJSObject(Local<Value> value) {
auto obj = value.As<Object>();
DCHECK_GE(obj->InternalFieldCount(), HistogramImpl::kInternalFieldCount);
Expand Down
35 changes: 35 additions & 0 deletions src/histogram.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ class HistogramImpl {
static void GetPercentilesBigInt(
const v8::FunctionCallbackInfo<v8::Value>& args);

static void FastReset(v8::Local<v8::Value> receiver);
static double FastGetCount(v8::Local<v8::Value> receiver);
static double FastGetMin(v8::Local<v8::Value> receiver);
static double FastGetMax(v8::Local<v8::Value> receiver);
static double FastGetMean(v8::Local<v8::Value> receiver);
static double FastGetExceeds(v8::Local<v8::Value> receiver);
static double FastGetStddev(v8::Local<v8::Value> receiver);
static double FastGetPercentile(v8::Local<v8::Value> receiver,
const double percentile);

static void AddMethods(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> tmpl);

Expand All @@ -110,6 +120,15 @@ class HistogramImpl {

private:
std::shared_ptr<Histogram> 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 {
Expand Down Expand Up @@ -138,6 +157,12 @@ class HistogramBase final : public BaseObject, public HistogramImpl {
static void RecordDelta(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Add(const v8::FunctionCallbackInfo<v8::Value>& args);

static void FastRecord(
v8::Local<v8::Value> receiver,
const int64_t value,
v8::FastApiCallbackOptions& options); // NOLINT(runtime/references)
static void FastRecordDelta(v8::Local<v8::Value> receiver);

HistogramBase(
Environment* env,
v8::Local<v8::Object> wrap,
Expand Down Expand Up @@ -173,6 +198,10 @@ class HistogramBase final : public BaseObject, public HistogramImpl {
private:
std::shared_ptr<Histogram> histogram_;
};

private:
static v8::CFunction fast_record_;
static v8::CFunction fast_record_delta_;
};

class IntervalHistogram final : public HandleWrap, public HistogramImpl {
Expand Down Expand Up @@ -204,6 +233,9 @@ class IntervalHistogram final : public HandleWrap, public HistogramImpl {
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);

static void FastStart(v8::Local<v8::Value> receiver, bool reset);
static void FastStop(v8::Local<v8::Value> receiver);

TransferMode GetTransferMode() const override {
return TransferMode::kCloneable;
}
Expand All @@ -222,6 +254,9 @@ class IntervalHistogram final : public HandleWrap, public HistogramImpl {
int32_t interval_ = 0;
std::function<void(Histogram&)> on_interval_;
uv_timer_t timer_;

static v8::CFunction fast_start_;
static v8::CFunction fast_stop_;
};

} // namespace node
Expand Down
9 changes: 9 additions & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ using CFunctionCallbackWithStrings =
const v8::FastOneByteString& base);
using CFunctionWithUint32 = uint32_t (*)(v8::Local<v8::Value>,
const uint32_t input);
using CFunctionWithDoubleReturnDouble = double (*)(v8::Local<v8::Value>,
const double);
using CFunctionWithInt64Fallback = void (*)(v8::Local<v8::Value>,
const int64_t,
v8::FastApiCallbackOptions&);
using CFunctionWithBool = void (*)(v8::Local<v8::Value>, bool);

// This class manages the external references from the V8 heap
// to the C++ addresses in Node.js.
Expand All @@ -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) \
Expand Down

0 comments on commit 5140cd3

Please sign in to comment.