Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: add fast api for Histogram #51296

Merged
merged 1 commit into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Comment on lines +18 to +25
Copy link
Member

@H4ad H4ad Dec 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I think you should have two variables to store the max/mean and then assert those variables to avoid deadcode elimination.

}
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);

BaseObject::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
Loading