summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames M Snell <jasnell@gmail.com>2021-12-11 16:59:48 -0800
committerJames M Snell <jasnell@gmail.com>2021-12-19 09:56:36 -0800
commit23637e9a3b208892449268290143dad4391fee45 (patch)
treec8f83283bb0d357b7db22298e217cbe5e877a656
parent665b404e6512c6fdfe811e793d4f14bf8f8a7d36 (diff)
downloadnode-new-23637e9a3b208892449268290143dad4391fee45.tar.gz
perf_hooks: multiple fixes for Histogram
* The createHistogram(options) options weren't actually implemented * Add a new count property that tracks the number of samples * Adds BigInt options for relevant properties * Adds add(other) method for RecordableHistogram * Cleans up and expands tests * Eliminates unnecessary ELDHistogram native class * Improve/Simplify histogram transfer impl Signed-off-by: James M Snell <jasnell@gmail.com> perf_hooks: simplify Histogram constructor options Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/41153 Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
-rw-r--r--doc/api/perf_hooks.md89
-rw-r--r--lib/internal/histogram.js259
-rw-r--r--lib/internal/perf/event_loop_delay.js41
-rw-r--r--src/histogram-inl.h49
-rw-r--r--src/histogram.cc280
-rw-r--r--src/histogram.h83
-rw-r--r--src/node_perf.cc67
-rw-r--r--src/node_perf.h17
-rw-r--r--test/parallel/test-perf-hooks-histogram.js133
9 files changed, 807 insertions, 211 deletions
diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md
index 34d2a58bd0..d3978958c5 100644
--- a/doc/api/perf_hooks.md
+++ b/doc/api/perf_hooks.md
@@ -817,10 +817,11 @@ added:
-->
* `options` {Object}
- * `min` {number|bigint} The minimum recordable value. Must be an integer
+ * `lowest` {number|bigint} The lowest discernible value. Must be an integer
value greater than 0. **Default:** `1`.
- * `max` {number|bigint} The maximum recordable value. Must be an integer
- value greater than `min`. **Default:** `Number.MAX_SAFE_INTEGER`.
+ * `highest` {number|bigint} The highest recordable value. Must be an integer
+ value that is equal to or greater than two times `min`.
+ **Default:** `Number.MAX_SAFE_INTEGER`.
* `figures` {number} The number of accuracy digits. Must be a number between
`1` and `5`. **Default:** `3`.
* Returns {RecordableHistogram}
@@ -870,6 +871,26 @@ console.log(h.percentile(99));
added: v11.10.0
-->
+### `histogram.count`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {number}
+
+The number of samples recorded by the histogram.
+
+### `histogram.countBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The number of samples recorded by the histogram.
+
### `histogram.exceeds`
<!-- YAML
@@ -881,6 +902,17 @@ added: v11.10.0
The number of times the event loop delay exceeded the maximum 1 hour event
loop delay threshold.
+### `histogram.exceedsBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The number of times the event loop delay exceeded the maximum 1 hour event
+loop delay threshold.
+
### `histogram.max`
<!-- YAML
@@ -891,6 +923,16 @@ added: v11.10.0
The maximum recorded event loop delay.
+### `histogram.maxBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The maximum recorded event loop delay.
+
### `histogram.mean`
<!-- YAML
@@ -911,6 +953,16 @@ added: v11.10.0
The minimum recorded event loop delay.
+### `histogram.minBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The minimum recorded event loop delay.
+
### `histogram.percentile(percentile)`
<!-- YAML
@@ -922,6 +974,17 @@ added: v11.10.0
Returns the value at the given percentile.
+### `histogram.percentileBigInt(percentile)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `percentile` {number} A percentile value in the range (0, 100).
+* Returns: {bigint}
+
+Returns the value at the given percentile.
+
### `histogram.percentiles`
<!-- YAML
@@ -932,6 +995,16 @@ added: v11.10.0
Returns a `Map` object detailing the accumulated percentile distribution.
+### `histogram.percentilesBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {Map}
+
+Returns a `Map` object detailing the accumulated percentile distribution.
+
### `histogram.reset()`
<!-- YAML
@@ -990,6 +1063,16 @@ added:
- v14.18.0
-->
+### `histogram.add(other)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `other` {RecordableHistogram}
+
+Adds the values from `other` to this histogram.
+
### `histogram.record(val)`
<!-- YAML
diff --git a/lib/internal/histogram.js b/lib/internal/histogram.js
index f437bfd4d7..4237e716dc 100644
--- a/lib/internal/histogram.js
+++ b/lib/internal/histogram.js
@@ -1,10 +1,12 @@
'use strict';
const {
+ MapPrototypeEntries,
NumberIsNaN,
NumberIsInteger,
NumberMAX_SAFE_INTEGER,
- ObjectSetPrototypeOf,
+ ObjectFromEntries,
+ ReflectConstruct,
SafeMap,
Symbol,
} = primordials;
@@ -24,33 +26,36 @@ const {
ERR_ILLEGAL_CONSTRUCTOR,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARG_TYPE,
+ ERR_INVALID_THIS,
ERR_OUT_OF_RANGE,
},
} = require('internal/errors');
const {
+ validateInteger,
validateNumber,
+ validateObject,
+ validateUint32,
} = require('internal/validators');
const kDestroy = Symbol('kDestroy');
const kHandle = Symbol('kHandle');
const kMap = Symbol('kMap');
+const kRecordable = Symbol('kRecordable');
const {
kClone,
kDeserialize,
- JSTransferable,
+ makeTransferable,
} = require('internal/worker/js_transferable');
function isHistogram(object) {
return object?.[kHandle] !== undefined;
}
-class Histogram extends JSTransferable {
- constructor(internal) {
- super();
- this[kHandle] = internal;
- this[kMap] = new SafeMap();
+class Histogram {
+ constructor() {
+ throw new ERR_ILLEGAL_CONSTRUCTOR();
}
[kInspect](depth, options) {
@@ -68,31 +73,118 @@ class Histogram extends JSTransferable {
mean: this.mean,
exceeds: this.exceeds,
stddev: this.stddev,
+ count: this.count,
percentiles: this.percentiles,
}, opts)}`;
}
+ /**
+ * @readonly
+ * @type {number}
+ */
+ get count() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ return this[kHandle]?.count();
+ }
+
+ /**
+ * @readonly
+ * @type {bigint}
+ */
+ get countBigInt() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ return this[kHandle]?.countBigInt();
+ }
+
+ /**
+ * @readonly
+ * @type {number}
+ */
get min() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
return this[kHandle]?.min();
}
+ /**
+ * @readonly
+ * @type {bigint}
+ */
+ get minBigInt() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ return this[kHandle]?.minBigInt();
+ }
+
+ /**
+ * @readonly
+ * @type {number}
+ */
get max() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
return this[kHandle]?.max();
}
+ /**
+ * @readonly
+ * @type {bigint}
+ */
+ get maxBigInt() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ return this[kHandle]?.maxBigInt();
+ }
+
+ /**
+ * @readonly
+ * @type {number}
+ */
get mean() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
return this[kHandle]?.mean();
}
+ /**
+ * @readonly
+ * @type {number}
+ */
get exceeds() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
return this[kHandle]?.exceeds();
}
+ /**
+ * @readonly
+ * @type {bigint}
+ */
+ get exceedsBigInt() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ return this[kHandle]?.exceedsBigInt();
+ }
+
+ /**
+ * @readonly
+ * @type {number}
+ */
get stddev() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
return this[kHandle]?.stddev();
}
+ /**
+ * @param {number} percentile
+ * @returns {number}
+ */
percentile(percentile) {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
validateNumber(percentile, 'percentile');
if (NumberIsNaN(percentile) || percentile <= 0 || percentile > 100)
@@ -101,31 +193,77 @@ class Histogram extends JSTransferable {
return this[kHandle]?.percentile(percentile);
}
+ /**
+ * @param {number} percentile
+ * @returns {bigint}
+ */
+ percentileBigInt(percentile) {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ validateNumber(percentile, 'percentile');
+
+ if (NumberIsNaN(percentile) || percentile <= 0 || percentile > 100)
+ throw new ERR_INVALID_ARG_VALUE.RangeError('percentile', percentile);
+
+ return this[kHandle]?.percentileBigInt(percentile);
+ }
+
+ /**
+ * @readonly
+ * @type {Map<number,number>}
+ */
get percentiles() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
this[kMap].clear();
this[kHandle]?.percentiles(this[kMap]);
return this[kMap];
}
- reset() {
- this[kHandle]?.reset();
+ /**
+ * @readonly
+ * @type {Map<number,bigint>}
+ */
+ get percentilesBigInt() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ this[kMap].clear();
+ this[kHandle]?.percentilesBigInt(this[kMap]);
+ return this[kMap];
}
- [kDestroy]() {
- this[kHandle] = undefined;
+ /**
+ * @returns {void}
+ */
+ reset() {
+ if (!isHistogram(this))
+ throw new ERR_INVALID_THIS('Histogram');
+ this[kHandle]?.reset();
}
[kClone]() {
const handle = this[kHandle];
return {
data: { handle },
- deserializeInfo: 'internal/histogram:InternalHistogram'
+ deserializeInfo: 'internal/histogram:internalHistogram'
};
}
[kDeserialize]({ handle }) {
this[kHandle] = handle;
}
+
+ toJSON() {
+ return {
+ count: this.count,
+ min: this.min,
+ max: this.max,
+ mean: this.mean,
+ exceeds: this.exceeds,
+ stddev: this.stddev,
+ percentiles: ObjectFromEntries(MapPrototypeEntries(this.percentiles))
+ };
+ }
}
class RecordableHistogram extends Histogram {
@@ -133,7 +271,13 @@ class RecordableHistogram extends Histogram {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
+ /**
+ * @param {number|bigint} val
+ * @returns {void}
+ */
record(val) {
+ if (this[kRecordable] === undefined)
+ throw new ERR_INVALID_THIS('RecordableHistogram');
if (typeof val === 'bigint') {
this[kHandle]?.record(val);
return;
@@ -148,56 +292,93 @@ class RecordableHistogram extends Histogram {
this[kHandle]?.record(val);
}
+ /**
+ * @returns {void}
+ */
recordDelta() {
+ if (this[kRecordable] === undefined)
+ throw new ERR_INVALID_THIS('RecordableHistogram');
this[kHandle]?.recordDelta();
}
+ /**
+ * @param {RecordableHistogram} other
+ */
+ add(other) {
+ if (this[kRecordable] === undefined)
+ throw new ERR_INVALID_THIS('RecordableHistogram');
+ if (other[kRecordable] === undefined)
+ throw new ERR_INVALID_ARG_TYPE('other', 'RecordableHistogram', other);
+ this[kHandle]?.add(other[kHandle]);
+ }
+
[kClone]() {
const handle = this[kHandle];
return {
data: { handle },
- deserializeInfo: 'internal/histogram:InternalRecordableHistogram'
+ deserializeInfo: 'internal/histogram:internalRecordableHistogram'
};
}
-}
-class InternalHistogram extends JSTransferable {
- constructor(handle) {
- super();
+ [kDeserialize]({ handle }) {
this[kHandle] = handle;
- this[kMap] = new SafeMap();
}
}
-class InternalRecordableHistogram extends JSTransferable {
- constructor(handle) {
- super();
- this[kHandle] = handle;
- this[kMap] = new SafeMap();
- }
+function internalHistogram(handle) {
+ return makeTransferable(ReflectConstruct(
+ function() {
+ this[kHandle] = handle;
+ this[kMap] = new SafeMap();
+ }, [], Histogram));
}
-
-InternalHistogram.prototype.constructor = Histogram;
-ObjectSetPrototypeOf(
- InternalHistogram.prototype,
- Histogram.prototype);
-
-InternalRecordableHistogram.prototype.constructor = RecordableHistogram;
-ObjectSetPrototypeOf(
- InternalRecordableHistogram.prototype,
- RecordableHistogram.prototype);
-
-function createHistogram() {
- return new InternalRecordableHistogram(new _Histogram());
+internalHistogram.prototype[kDeserialize] = () => {};
+
+function internalRecordableHistogram(handle) {
+ return makeTransferable(ReflectConstruct(
+ function() {
+ this[kHandle] = handle;
+ this[kMap] = new SafeMap();
+ this[kRecordable] = true;
+ }, [], RecordableHistogram));
+}
+internalRecordableHistogram.prototype[kDeserialize] = () => {};
+
+/**
+ * @param {{
+ * lowest? : number,
+ * highest? : number,
+ * figures? : number
+ * }} [options]
+ * @returns {RecordableHistogram}
+ */
+function createHistogram(options = {}) {
+ validateObject(options, 'options');
+ const {
+ lowest = 1,
+ highest = NumberMAX_SAFE_INTEGER,
+ figures = 3,
+ } = options;
+ if (typeof lowest !== 'bigint')
+ validateInteger(lowest, 'options.lowest', 1, NumberMAX_SAFE_INTEGER);
+ if (typeof highest !== 'bigint') {
+ validateInteger(highest, 'options.highest',
+ 2 * lowest, NumberMAX_SAFE_INTEGER);
+ } else if (highest < 2n * lowest) {
+ throw new ERR_INVALID_ARG_VALUE.RangeError('options.highest', highest);
+ }
+ validateUint32(figures, 'options.figures', 1, 5);
+ return internalRecordableHistogram(new _Histogram(lowest, highest, figures));
}
module.exports = {
Histogram,
RecordableHistogram,
- InternalHistogram,
- InternalRecordableHistogram,
+ internalHistogram,
+ internalRecordableHistogram,
isHistogram,
kDestroy,
kHandle,
+ kMap,
createHistogram,
};
diff --git a/lib/internal/perf/event_loop_delay.js b/lib/internal/perf/event_loop_delay.js
index f5d0eb74d5..efd45f4ed7 100644
--- a/lib/internal/perf/event_loop_delay.js
+++ b/lib/internal/perf/event_loop_delay.js
@@ -1,16 +1,19 @@
'use strict';
const {
+ ReflectConstruct,
+ SafeMap,
Symbol,
} = primordials;
const {
codes: {
ERR_ILLEGAL_CONSTRUCTOR,
+ ERR_INVALID_THIS,
}
} = require('internal/errors');
const {
- ELDHistogram: _ELDHistogram,
+ createELDHistogram,
} = internalBinding('performance');
const {
@@ -21,25 +24,38 @@ const {
const {
Histogram,
kHandle,
+ kMap,
} = require('internal/histogram');
+const {
+ makeTransferable,
+} = require('internal/worker/js_transferable');
+
const kEnabled = Symbol('kEnabled');
class ELDHistogram extends Histogram {
constructor(i) {
- if (!(i instanceof _ELDHistogram)) {
- throw new ERR_ILLEGAL_CONSTRUCTOR();
- }
- super(i);
- this[kEnabled] = false;
+ throw new ERR_ILLEGAL_CONSTRUCTOR();
}
+
+ /**
+ * @returns {boolean}
+ */
enable() {
+ if (this[kEnabled] === undefined)
+ throw new ERR_INVALID_THIS('ELDHistogram');
if (this[kEnabled]) return false;
this[kEnabled] = true;
this[kHandle].start();
return true;
}
+
+ /**
+ * @returns {boolean}
+ */
disable() {
+ if (this[kEnabled] === undefined)
+ throw new ERR_INVALID_THIS('ELDHistogram');
if (!this[kEnabled]) return false;
this[kEnabled] = false;
this[kHandle].stop();
@@ -47,13 +63,24 @@ class ELDHistogram extends Histogram {
}
}
+/**
+ * @param {{
+ * resolution : number
+ * }} [options]
+ * @returns {ELDHistogram}
+ */
function monitorEventLoopDelay(options = {}) {
validateObject(options, 'options');
const { resolution = 10 } = options;
validateInteger(resolution, 'options.resolution', 1);
- return new ELDHistogram(new _ELDHistogram(resolution));
+ return makeTransferable(ReflectConstruct(
+ function() {
+ this[kEnabled] = false;
+ this[kHandle] = createELDHistogram(resolution);
+ this[kMap] = new SafeMap();
+ }, [], ELDHistogram));
}
module.exports = monitorEventLoopDelay;
diff --git a/src/histogram-inl.h b/src/histogram-inl.h
index 18a1668512..3b8712c878 100644
--- a/src/histogram-inl.h
+++ b/src/histogram-inl.h
@@ -13,35 +13,49 @@ void Histogram::Reset() {
Mutex::ScopedLock lock(mutex_);
hdr_reset(histogram_.get());
exceeds_ = 0;
+ count_ = 0;
prev_ = 0;
}
-int64_t Histogram::Min() {
+double Histogram::Add(const Histogram& other) {
+ Mutex::ScopedLock lock(mutex_);
+ count_ += other.count_;
+ exceeds_ += other.exceeds_;
+ if (other.prev_ > prev_)
+ prev_ = other.prev_;
+ return static_cast<double>(hdr_add(histogram_.get(), other.histogram_.get()));
+}
+
+size_t Histogram::Count() const {
+ Mutex::ScopedLock lock(mutex_);
+ return count_;
+}
+
+int64_t Histogram::Min() const {
Mutex::ScopedLock lock(mutex_);
return hdr_min(histogram_.get());
}
-int64_t Histogram::Max() {
+int64_t Histogram::Max() const {
Mutex::ScopedLock lock(mutex_);
return hdr_max(histogram_.get());
}
-double Histogram::Mean() {
+double Histogram::Mean() const {
Mutex::ScopedLock lock(mutex_);
return hdr_mean(histogram_.get());
}
-double Histogram::Stddev() {
+double Histogram::Stddev() const {
Mutex::ScopedLock lock(mutex_);
return hdr_stddev(histogram_.get());
}
-double Histogram::Percentile(double percentile) {
+int64_t Histogram::Percentile(double percentile) const {
Mutex::ScopedLock lock(mutex_);
CHECK_GT(percentile, 0);
CHECK_LE(percentile, 100);
- return static_cast<double>(
- hdr_value_at_percentile(histogram_.get(), percentile));
+ return hdr_value_at_percentile(histogram_.get(), percentile);
}
template <typename Iterator>
@@ -51,26 +65,31 @@ void Histogram::Percentiles(Iterator&& fn) {
hdr_iter_percentile_init(&iter, histogram_.get(), 1);
while (hdr_iter_next(&iter)) {
double key = iter.specifics.percentiles.percentile;
- double value = static_cast<double>(iter.value);
- fn(key, value);
+ fn(key, iter.value);
}
}
bool Histogram::Record(int64_t value) {
Mutex::ScopedLock lock(mutex_);
- return hdr_record_value(histogram_.get(), value);
+ bool recorded = hdr_record_value(histogram_.get(), value);
+ if (!recorded)
+ exceeds_++;
+ else
+ count_++;
+ return recorded;
}
uint64_t Histogram::RecordDelta() {
Mutex::ScopedLock lock(mutex_);
uint64_t time = uv_hrtime();
- uint64_t delta = 0;
+ int64_t delta = 0;
if (prev_ > 0) {
+ CHECK_GE(time, prev_);
delta = time - prev_;
- if (delta > 0) {
- if (!hdr_record_value(histogram_.get(), delta) && exceeds_ < 0xFFFFFFFF)
- exceeds_++;
- }
+ if (hdr_record_value(histogram_.get(), delta))
+ count_++;
+ else
+ exceeds_++;
}
prev_ = time;
return delta;
diff --git a/src/histogram.cc b/src/histogram.cc
index 6fbb0eda6c..87641a4a36 100644
--- a/src/histogram.cc
+++ b/src/histogram.cc
@@ -10,16 +10,21 @@ namespace node {
using v8::BigInt;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
+using v8::Integer;
using v8::Local;
using v8::Map;
using v8::Number;
using v8::Object;
using v8::String;
+using v8::Uint32;
using v8::Value;
-Histogram::Histogram(int64_t lowest, int64_t highest, int figures) {
+Histogram::Histogram(const Options& options) {
hdr_histogram* histogram;
- CHECK_EQ(0, hdr_init(lowest, highest, figures, &histogram));
+ CHECK_EQ(0, hdr_init(options.lowest,
+ options.highest,
+ options.figures,
+ &histogram));
histogram_.reset(histogram);
}
@@ -27,8 +32,8 @@ void Histogram::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackFieldWithSize("histogram", GetMemorySize());
}
-HistogramImpl::HistogramImpl(int64_t lowest, int64_t highest, int figures)
- : histogram_(new Histogram(lowest, highest, figures)) {}
+HistogramImpl::HistogramImpl(const Histogram::Options& options)
+ : histogram_(new Histogram(options)) {}
HistogramImpl::HistogramImpl(std::shared_ptr<Histogram> histogram)
: histogram_(std::move(histogram)) {}
@@ -36,11 +41,9 @@ HistogramImpl::HistogramImpl(std::shared_ptr<Histogram> histogram)
HistogramBase::HistogramBase(
Environment* env,
Local<Object> wrap,
- int64_t lowest,
- int64_t highest,
- int figures)
+ const Histogram::Options& options)
: BaseObject(env, wrap),
- HistogramImpl(lowest, highest, figures) {
+ HistogramImpl(options) {
MakeWeak();
}
@@ -57,6 +60,22 @@ void HistogramBase::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("histogram", histogram());
}
+void HistogramBase::GetCount(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ double value = static_cast<double>((*histogram)->Count());
+ args.GetReturnValue().Set(value);
+}
+
+void HistogramBase::GetCountBigInt(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(
+ BigInt::NewFromUnsigned(env->isolate(), (*histogram)->Count()));
+}
+
void HistogramBase::GetMin(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -64,6 +83,13 @@ void HistogramBase::GetMin(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(value);
}
+void HistogramBase::GetMinBigInt(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(BigInt::New(env->isolate(), (*histogram)->Min()));
+}
+
void HistogramBase::GetMax(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -71,6 +97,14 @@ void HistogramBase::GetMax(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(value);
}
+void HistogramBase::GetMaxBigInt(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(
+ BigInt::New(env->isolate(), (*histogram)->Max()));
+}
+
void HistogramBase::GetMean(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -84,6 +118,14 @@ void HistogramBase::GetExceeds(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(value);
}
+void HistogramBase::GetExceedsBigInt(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(
+ BigInt::NewFromUnsigned(env->isolate(), (*histogram)->Exceeds()));
+}
+
void HistogramBase::GetStddev(const FunctionCallbackInfo<Value>& args) {
HistogramBase* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -95,7 +137,19 @@ void HistogramBase::GetPercentile(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
CHECK(args[0]->IsNumber());
double percentile = args[0].As<Number>()->Value();
- args.GetReturnValue().Set((*histogram)->Percentile(percentile));
+ double value = static_cast<double>((*histogram)->Percentile(percentile));
+ args.GetReturnValue().Set(value);
+}
+
+void HistogramBase::GetPercentileBigInt(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ CHECK(args[0]->IsNumber());
+ double percentile = args[0].As<Number>()->Value();
+ int64_t value = (*histogram)->Percentile(percentile);
+ args.GetReturnValue().Set(BigInt::New(env->isolate(), value));
}
void HistogramBase::GetPercentiles(const FunctionCallbackInfo<Value>& args) {
@@ -104,11 +158,26 @@ void HistogramBase::GetPercentiles(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
CHECK(args[0]->IsMap());
Local<Map> map = args[0].As<Map>();
- (*histogram)->Percentiles([map, env](double key, double value) {
+ (*histogram)->Percentiles([map, env](double key, int64_t value) {
+ map->Set(
+ env->context(),
+ Number::New(env->isolate(), key),
+ Number::New(env->isolate(), static_cast<double>(value))).IsEmpty();
+ });
+}
+
+void HistogramBase::GetPercentilesBigInt(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ CHECK(args[0]->IsMap());
+ Local<Map> map = args[0].As<Map>();
+ (*histogram)->Percentiles([map, env](double key, int64_t value) {
map->Set(
env->context(),
Number::New(env->isolate(), key),
- Number::New(env->isolate(), value)).IsEmpty();
+ BigInt::New(env->isolate(), value)).IsEmpty();
});
}
@@ -138,11 +207,22 @@ void HistogramBase::Record(const FunctionCallbackInfo<Value>& args) {
(*histogram)->Record(value);
}
+void HistogramBase::Add(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ HistogramBase* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+
+ CHECK(GetConstructorTemplate(env)->HasInstance(args[0]));
+ HistogramBase* other;
+ ASSIGN_OR_RETURN_UNWRAP(&other, args[0]);
+
+ double count = (*histogram)->Add(*(other->histogram()));
+ args.GetReturnValue().Set(count);
+}
+
BaseObjectPtr<HistogramBase> HistogramBase::Create(
Environment* env,
- int64_t lowest,
- int64_t highest,
- int figures) {
+ const Histogram::Options& options) {
Local<Object> obj;
if (!GetConstructorTemplate(env)
->InstanceTemplate()
@@ -150,8 +230,7 @@ BaseObjectPtr<HistogramBase> HistogramBase::Create(
return BaseObjectPtr<HistogramBase>();
}
- return MakeBaseObject<HistogramBase>(
- env, obj, lowest, highest, figures);
+ return MakeBaseObject<HistogramBase>(env, obj, options);
}
BaseObjectPtr<HistogramBase> HistogramBase::Create(
@@ -169,7 +248,32 @@ BaseObjectPtr<HistogramBase> HistogramBase::Create(
void HistogramBase::New(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
Environment* env = Environment::GetCurrent(args);
- new HistogramBase(env, args.This());
+
+ CHECK_IMPLIES(!args[0]->IsNumber(), args[0]->IsBigInt());
+ CHECK_IMPLIES(!args[1]->IsNumber(), args[1]->IsBigInt());
+ CHECK(args[2]->IsUint32());
+
+ int64_t lowest = 1;
+ int64_t highest = std::numeric_limits<int64_t>::max();
+
+ bool lossless_ignored;
+
+ if (args[0]->IsNumber()) {
+ lowest = args[0].As<Integer>()->Value();
+ } else if (args[0]->IsBigInt()) {
+ lowest = args[0].As<BigInt>()->Int64Value(&lossless_ignored);
+ }
+
+ if (args[1]->IsNumber()) {
+ highest = args[1].As<Integer>()->Value();
+ } else if (args[1]->IsBigInt()) {
+ highest = args[1].As<BigInt>()->Int64Value(&lossless_ignored);
+ }
+
+ int32_t figures = args[2].As<Uint32>()->Value();
+ new HistogramBase(env, args.This(), Histogram::Options {
+ lowest, highest, figures
+ });
}
Local<FunctionTemplate> HistogramBase::GetConstructorTemplate(
@@ -184,16 +288,26 @@ Local<FunctionTemplate> HistogramBase::GetConstructorTemplate(
tmpl->InstanceTemplate()->SetInternalFieldCount(
HistogramBase::kInternalFieldCount);
+ env->SetProtoMethodNoSideEffect(tmpl, "count", GetCount);
+ env->SetProtoMethodNoSideEffect(tmpl, "countBigInt", GetCountBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "exceeds", GetExceeds);
+ env->SetProtoMethodNoSideEffect(tmpl, "exceedsBigInt", GetExceedsBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "min", GetMin);
+ env->SetProtoMethodNoSideEffect(tmpl, "minBigInt", GetMinBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "max", GetMax);
+ env->SetProtoMethodNoSideEffect(tmpl, "maxBigInt", GetMaxBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "mean", GetMean);
env->SetProtoMethodNoSideEffect(tmpl, "stddev", GetStddev);
env->SetProtoMethodNoSideEffect(tmpl, "percentile", GetPercentile);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentileBigInt",
+ GetPercentileBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "percentiles", GetPercentiles);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentilesBigInt",
+ GetPercentilesBigInt);
env->SetProtoMethod(tmpl, "reset", DoReset);
env->SetProtoMethod(tmpl, "record", Record);
env->SetProtoMethod(tmpl, "recordDelta", RecordDelta);
+ env->SetProtoMethod(tmpl, "add", Add);
env->set_histogram_ctor_template(tmpl);
}
return tmpl;
@@ -202,16 +316,24 @@ Local<FunctionTemplate> HistogramBase::GetConstructorTemplate(
void HistogramBase::RegisterExternalReferences(
ExternalReferenceRegistry* registry) {
registry->Register(New);
+ registry->Register(GetCount);
+ registry->Register(GetCountBigInt);
registry->Register(GetExceeds);
+ registry->Register(GetExceedsBigInt);
registry->Register(GetMin);
+ registry->Register(GetMinBigInt);
registry->Register(GetMax);
+ registry->Register(GetMaxBigInt);
registry->Register(GetMean);
registry->Register(GetStddev);
registry->Register(GetPercentile);
+ registry->Register(GetPercentileBigInt);
registry->Register(GetPercentiles);
+ registry->Register(GetPercentilesBigInt);
registry->Register(DoReset);
registry->Register(Record);
registry->Register(RecordDelta);
+ registry->Register(Add);
}
void HistogramBase::Initialize(Environment* env, Local<Object> target) {
@@ -242,13 +364,22 @@ Local<FunctionTemplate> IntervalHistogram::GetConstructorTemplate(
tmpl->Inherit(HandleWrap::GetConstructorTemplate(env));
tmpl->InstanceTemplate()->SetInternalFieldCount(
HistogramBase::kInternalFieldCount);
+ env->SetProtoMethodNoSideEffect(tmpl, "count", GetCount);
+ env->SetProtoMethodNoSideEffect(tmpl, "countBigInt", GetCountBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "exceeds", GetExceeds);
+ env->SetProtoMethodNoSideEffect(tmpl, "exceedsBigInt", GetExceedsBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "min", GetMin);
+ env->SetProtoMethodNoSideEffect(tmpl, "minBigInt", GetMinBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "max", GetMax);
+ env->SetProtoMethodNoSideEffect(tmpl, "maxBigInt", GetMaxBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "mean", GetMean);
env->SetProtoMethodNoSideEffect(tmpl, "stddev", GetStddev);
env->SetProtoMethodNoSideEffect(tmpl, "percentile", GetPercentile);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentileBigInt",
+ GetPercentileBigInt);
env->SetProtoMethodNoSideEffect(tmpl, "percentiles", GetPercentiles);
+ env->SetProtoMethodNoSideEffect(tmpl, "percentilesBigInt",
+ GetPercentilesBigInt);
env->SetProtoMethod(tmpl, "reset", DoReset);
env->SetProtoMethod(tmpl, "start", Start);
env->SetProtoMethod(tmpl, "stop", Stop);
@@ -259,13 +390,20 @@ Local<FunctionTemplate> IntervalHistogram::GetConstructorTemplate(
void IntervalHistogram::RegisterExternalReferences(
ExternalReferenceRegistry* registry) {
+ registry->Register(GetCount);
+ registry->Register(GetCountBigInt);
registry->Register(GetExceeds);
+ registry->Register(GetExceedsBigInt);
registry->Register(GetMin);
+ registry->Register(GetMinBigInt);
registry->Register(GetMax);
+ registry->Register(GetMaxBigInt);
registry->Register(GetMean);
registry->Register(GetStddev);
registry->Register(GetPercentile);
+ registry->Register(GetPercentileBigInt);
registry->Register(GetPercentiles);
+ registry->Register(GetPercentilesBigInt);
registry->Register(DoReset);
registry->Register(Start);
registry->Register(Stop);
@@ -276,24 +414,48 @@ IntervalHistogram::IntervalHistogram(
Local<Object> wrap,
AsyncWrap::ProviderType type,
int32_t interval,
- int64_t lowest,
- int64_t highest,
- int figures)
+ std::function<void(Histogram&)> on_interval,
+ const Histogram::Options& options)
: HandleWrap(
env,
wrap,
reinterpret_cast<uv_handle_t*>(&timer_),
type),
- HistogramImpl(lowest, highest, figures),
- interval_(interval) {
+ HistogramImpl(options),
+ interval_(interval),
+ on_interval_(std::move(on_interval)) {
MakeWeak();
uv_timer_init(env->event_loop(), &timer_);
}
+BaseObjectPtr<IntervalHistogram> IntervalHistogram::Create(
+ Environment* env,
+ int32_t interval,
+ std::function<void(Histogram&)> on_interval,
+ const Histogram::Options& options) {
+ Local<Object> obj;
+ if (!GetConstructorTemplate(env)
+ ->InstanceTemplate()
+ ->NewInstance(env->context()).ToLocal(&obj)) {
+ return BaseObjectPtr<IntervalHistogram>();
+ }
+
+ return MakeBaseObject<IntervalHistogram>(
+ env,
+ obj,
+ AsyncWrap::PROVIDER_ELDHISTOGRAM,
+ interval,
+ std::move(on_interval),
+ options);
+}
+
void IntervalHistogram::TimerCB(uv_timer_t* handle) {
IntervalHistogram* histogram =
ContainerOf(&IntervalHistogram::timer_, handle);
- histogram->OnInterval();
+
+ Histogram* h = histogram->histogram().get();
+
+ histogram->on_interval_(*h);
}
void IntervalHistogram::MemoryInfo(MemoryTracker* tracker) const {
@@ -327,6 +489,22 @@ void IntervalHistogram::Stop(const FunctionCallbackInfo<Value>& args) {
histogram->OnStop();
}
+void IntervalHistogram::GetCount(const FunctionCallbackInfo<Value>& args) {
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ double value = static_cast<double>((*histogram)->Count());
+ args.GetReturnValue().Set(value);
+}
+
+void IntervalHistogram::GetCountBigInt(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(
+ BigInt::NewFromUnsigned(env->isolate(), (*histogram)->Count()));
+}
+
void IntervalHistogram::GetMin(const FunctionCallbackInfo<Value>& args) {
IntervalHistogram* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -334,6 +512,13 @@ void IntervalHistogram::GetMin(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(value);
}
+void IntervalHistogram::GetMinBigInt(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(BigInt::New(env->isolate(), (*histogram)->Min()));
+}
+
void IntervalHistogram::GetMax(const FunctionCallbackInfo<Value>& args) {
IntervalHistogram* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -341,6 +526,13 @@ void IntervalHistogram::GetMax(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(value);
}
+void IntervalHistogram::GetMaxBigInt(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(BigInt::New(env->isolate(), (*histogram)->Min()));
+}
+
void IntervalHistogram::GetMean(const FunctionCallbackInfo<Value>& args) {
IntervalHistogram* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -354,6 +546,15 @@ void IntervalHistogram::GetExceeds(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(value);
}
+void IntervalHistogram::GetExceedsBigInt(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ args.GetReturnValue().Set(
+ BigInt::New(env->isolate(), (*histogram)->Exceeds()));
+}
+
void IntervalHistogram::GetStddev(const FunctionCallbackInfo<Value>& args) {
IntervalHistogram* histogram;
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -365,7 +566,19 @@ void IntervalHistogram::GetPercentile(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
CHECK(args[0]->IsNumber());
double percentile = args[0].As<Number>()->Value();
- args.GetReturnValue().Set((*histogram)->Percentile(percentile));
+ double value = static_cast<double>((*histogram)->Percentile(percentile));
+ args.GetReturnValue().Set(value);
+}
+
+void IntervalHistogram::GetPercentileBigInt(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ CHECK(args[0]->IsNumber());
+ double percentile = args[0].As<Number>()->Value();
+ int64_t value = (*histogram)->Percentile(percentile);
+ args.GetReturnValue().Set(BigInt::New(env->isolate(), value));
}
void IntervalHistogram::GetPercentiles(
@@ -375,11 +588,26 @@ void IntervalHistogram::GetPercentiles(
ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
CHECK(args[0]->IsMap());
Local<Map> map = args[0].As<Map>();
- (*histogram)->Percentiles([map, env](double key, double value) {
+ (*histogram)->Percentiles([map, env](double key, int64_t value) {
+ map->Set(
+ env->context(),
+ Number::New(env->isolate(), key),
+ Number::New(env->isolate(), static_cast<double>(value))).IsEmpty();
+ });
+}
+
+void IntervalHistogram::GetPercentilesBigInt(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ IntervalHistogram* histogram;
+ ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+ CHECK(args[0]->IsMap());
+ Local<Map> map = args[0].As<Map>();
+ (*histogram)->Percentiles([map, env](double key, int64_t value) {
map->Set(
env->context(),
Number::New(env->isolate(), key),
- Number::New(env->isolate(), value)).IsEmpty();
+ BigInt::New(env->isolate(), value)).IsEmpty();
});
}
diff --git a/src/histogram.h b/src/histogram.h
index 00b4f7796f..d526bba8a1 100644
--- a/src/histogram.h
+++ b/src/histogram.h
@@ -24,23 +24,29 @@ constexpr int kDefaultHistogramFigures = 3;
class Histogram : public MemoryRetainer {
public:
- Histogram(
- int64_t lowest = 1,
- int64_t highest = std::numeric_limits<int64_t>::max(),
- int figures = kDefaultHistogramFigures);
+ struct Options {
+ int64_t lowest = 1;
+ int64_t highest = std::numeric_limits<int64_t>::max();
+ int figures = kDefaultHistogramFigures;
+ };
+
+ explicit Histogram(const Options& options);
virtual ~Histogram() = default;
inline bool Record(int64_t value);
inline void Reset();
- inline int64_t Min();
- inline int64_t Max();
- inline double Mean();
- inline double Stddev();
- inline double Percentile(double percentile);
- inline int64_t Exceeds() const { return exceeds_; }
+ inline int64_t Min() const;
+ inline int64_t Max() const;
+ inline double Mean() const;
+ inline double Stddev() const;
+ inline int64_t Percentile(double percentile) const;
+ inline size_t Exceeds() const { return exceeds_; }
+ inline size_t Count() const;
inline uint64_t RecordDelta();
+ inline double Add(const Histogram& other);
+
// Iterator is a function type that takes two doubles as argument, one for
// percentile and one for the value at that percentile.
template <typename Iterator>
@@ -55,20 +61,20 @@ class Histogram : public MemoryRetainer {
private:
using HistogramPointer = DeleteFnPtr<hdr_histogram, hdr_close>;
HistogramPointer histogram_;
- int64_t exceeds_ = 0;
uint64_t prev_ = 0;
-
+ size_t exceeds_ = 0;
+ size_t count_ = 0;
Mutex mutex_;
};
class HistogramImpl {
public:
- HistogramImpl(int64_t lowest, int64_t highest, int figures);
+ explicit HistogramImpl(
+ const Histogram::Options& options = Histogram::Options {});
explicit HistogramImpl(std::shared_ptr<Histogram> histogram);
Histogram* operator->() { return histogram_.get(); }
- protected:
const std::shared_ptr<Histogram>& histogram() const { return histogram_; }
private:
@@ -84,9 +90,7 @@ class HistogramBase : public BaseObject, public HistogramImpl {
static BaseObjectPtr<HistogramBase> Create(
Environment* env,
- int64_t lowest = 1,
- int64_t highest = std::numeric_limits<int64_t>::max(),
- int figures = kDefaultHistogramFigures);
+ const Histogram::Options& options = Histogram::Options {});
static BaseObjectPtr<HistogramBase> Create(
Environment* env,
@@ -98,6 +102,12 @@ class HistogramBase : public BaseObject, public HistogramImpl {
SET_MEMORY_INFO_NAME(HistogramBase)
SET_SELF_SIZE(HistogramBase)
+ static void GetCountBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetMinBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetMaxBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetExceedsBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ static void GetCount(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMin(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMax(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMean(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -105,18 +115,21 @@ class HistogramBase : public BaseObject, public HistogramImpl {
static void GetStddev(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPercentile(
const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetPercentileBigInt(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPercentiles(
const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetPercentilesBigInt(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
static void DoReset(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Record(const v8::FunctionCallbackInfo<v8::Value>& args);
static void RecordDelta(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Add(const v8::FunctionCallbackInfo<v8::Value>& args);
HistogramBase(
Environment* env,
v8::Local<v8::Object> wrap,
- int64_t lowest = 1,
- int64_t highest = std::numeric_limits<int64_t>::max(),
- int figures = kDefaultHistogramFigures);
+ const Histogram::Options& options = Histogram::Options {});
HistogramBase(
Environment* env,
@@ -164,23 +177,24 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
static BaseObjectPtr<IntervalHistogram> Create(
Environment* env,
- int64_t lowest = 1,
- int64_t highest = std::numeric_limits<int64_t>::max(),
- int figures = kDefaultHistogramFigures);
-
- virtual void OnInterval() = 0;
-
- void MemoryInfo(MemoryTracker* tracker) const override;
+ int32_t interval,
+ std::function<void(Histogram&)> on_interval,
+ const Histogram::Options& options);
IntervalHistogram(
Environment* env,
v8::Local<v8::Object> wrap,
AsyncWrap::ProviderType type,
int32_t interval,
- int64_t lowest = 1,
- int64_t highest = std::numeric_limits<int64_t>::max(),
- int figures = kDefaultHistogramFigures);
+ std::function<void(Histogram&)> on_interval,
+ const Histogram::Options& options = Histogram::Options {});
+
+ static void GetCountBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetMinBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetMaxBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetExceedsBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetCount(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMin(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMax(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetMean(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -188,8 +202,12 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
static void GetStddev(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPercentile(
const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetPercentileBigInt(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPercentiles(
const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void GetPercentilesBigInt(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
static void DoReset(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -199,6 +217,10 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
}
std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
+ void MemoryInfo(MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(IntervalHistogram)
+ SET_SELF_SIZE(IntervalHistogram)
+
private:
static void TimerCB(uv_timer_t* handle);
void OnStart(StartFlags flags = StartFlags::RESET);
@@ -206,6 +228,7 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
bool enabled_ = false;
int32_t interval_ = 0;
+ std::function<void(Histogram&)> on_interval_;
uv_timer_t timer_;
};
diff --git a/src/node_perf.cc b/src/node_perf.cc
index acbb0e0d90..8bda1791fc 100644
--- a/src/node_perf.cc
+++ b/src/node_perf.cc
@@ -234,51 +234,25 @@ void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(1.0 * idle_time / 1e6);
}
-// Event Loop Timing Histogram
-void ELDHistogram::New(const FunctionCallbackInfo<Value>& args) {
+void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- CHECK(args.IsConstructCall());
- int64_t resolution = args[0].As<Integer>()->Value();
- CHECK_GT(resolution, 0);
- new ELDHistogram(env, args.This(), resolution);
-}
-
-void ELDHistogram::Initialize(Environment* env, Local<Object> target) {
- Local<FunctionTemplate> tmpl = env->NewFunctionTemplate(New);
- tmpl->Inherit(IntervalHistogram::GetConstructorTemplate(env));
- tmpl->InstanceTemplate()->SetInternalFieldCount(
- ELDHistogram::kInternalFieldCount);
- env->SetConstructorFunction(target, "ELDHistogram", tmpl);
-}
-
-void ELDHistogram::RegisterExternalReferences(
- ExternalReferenceRegistry* registry) {
- registry->Register(New);
- IntervalHistogram::RegisterExternalReferences(registry);
-}
-
-ELDHistogram::ELDHistogram(
- Environment* env,
- Local<Object> wrap,
- int64_t interval)
- : IntervalHistogram(
- env,
- wrap,
- AsyncWrap::PROVIDER_ELDHISTOGRAM,
- interval, 1, 3.6e12, 3) {}
-
-void ELDHistogram::OnInterval() {
- uint64_t delta = histogram()->RecordDelta();
- TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "delay", delta);
- TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "min", histogram()->Min());
- TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "max", histogram()->Max());
- TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "mean", histogram()->Mean());
- TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
- "stddev", histogram()->Stddev());
+ int64_t interval = args[0].As<Integer>()->Value();
+ CHECK_GT(interval, 0);
+ BaseObjectPtr<IntervalHistogram> histogram =
+ IntervalHistogram::Create(env, interval, [](Histogram& histogram) {
+ uint64_t delta = histogram.RecordDelta();
+ TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+ "delay", delta);
+ TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+ "min", histogram.Min());
+ TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+ "max", histogram.Max());
+ TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+ "mean", histogram.Mean());
+ TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+ "stddev", histogram.Stddev());
+ }, Histogram::Options { 1000 });
+ args.GetReturnValue().Set(histogram->object());
}
void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) {
@@ -326,6 +300,7 @@ void Initialize(Local<Object> target,
env->SetMethod(target, "loopIdleTime", LoopIdleTime);
env->SetMethod(target, "getTimeOrigin", GetTimeOrigin);
env->SetMethod(target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
+ env->SetMethod(target, "createELDHistogram", CreateELDHistogram);
Local<Object> constants = Object::New(isolate);
@@ -368,7 +343,6 @@ void Initialize(Local<Object> target,
attr).ToChecked();
HistogramBase::Initialize(env, target);
- ELDHistogram::Initialize(env, target);
}
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
@@ -380,8 +354,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(LoopIdleTime);
registry->Register(GetTimeOrigin);
registry->Register(GetTimeOriginTimeStamp);
+ registry->Register(CreateELDHistogram);
HistogramBase::RegisterExternalReferences(registry);
- ELDHistogram::RegisterExternalReferences(registry);
+ IntervalHistogram::RegisterExternalReferences(registry);
}
} // namespace performance
} // namespace node
diff --git a/src/node_perf.h b/src/node_perf.h
index 64913ab9de..b1a9917138 100644
--- a/src/node_perf.h
+++ b/src/node_perf.h
@@ -160,23 +160,6 @@ struct GCPerformanceEntryTraits {
using GCPerformanceEntry = PerformanceEntry<GCPerformanceEntryTraits>;
-class ELDHistogram : public IntervalHistogram {
- public:
- static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
- static void Initialize(Environment* env, v8::Local<v8::Object> target);
- static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
-
- ELDHistogram(
- Environment* env,
- v8::Local<v8::Object> wrap,
- int64_t interval);
-
- void OnInterval() override;
-
- SET_MEMORY_INFO_NAME(ELDHistogram)
- SET_SELF_SIZE(ELDHistogram)
-};
-
} // namespace performance
} // namespace node
diff --git a/test/parallel/test-perf-hooks-histogram.js b/test/parallel/test-perf-hooks-histogram.js
index a60d3a94bb..2137c1b2a3 100644
--- a/test/parallel/test-perf-hooks-histogram.js
+++ b/test/parallel/test-perf-hooks-histogram.js
@@ -1,54 +1,75 @@
'use strict';
const common = require('../common');
-const assert = require('assert');
+
+const {
+ ok,
+ strictEqual,
+ throws,
+} = require('assert');
+
const {
createHistogram,
monitorEventLoopDelay,
} = require('perf_hooks');
+
const { inspect } = require('util');
{
const h = createHistogram();
- assert.strictEqual(h.min, 9223372036854776000);
- assert.strictEqual(h.max, 0);
- assert.strictEqual(h.exceeds, 0);
- assert(Number.isNaN(h.mean));
- assert(Number.isNaN(h.stddev));
+ strictEqual(h.min, 9223372036854776000);
+ strictEqual(h.minBigInt, 9223372036854775807n);
+ strictEqual(h.max, 0);
+ strictEqual(h.maxBigInt, 0n);
+ strictEqual(h.exceeds, 0);
+ strictEqual(h.exceedsBigInt, 0n);
+ ok(Number.isNaN(h.mean));
+ ok(Number.isNaN(h.stddev));
+
+ strictEqual(h.count, 0);
+ strictEqual(h.countBigInt, 0n);
h.record(1);
+ strictEqual(h.count, 1);
+ strictEqual(h.countBigInt, 1n);
+
[false, '', {}, undefined, null].forEach((i) => {
- assert.throws(() => h.record(i), {
+ throws(() => h.record(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
- assert.throws(() => h.record(0, Number.MAX_SAFE_INTEGER + 1), {
+ throws(() => h.record(0, Number.MAX_SAFE_INTEGER + 1), {
code: 'ERR_OUT_OF_RANGE'
});
- assert.strictEqual(h.min, 1);
- assert.strictEqual(h.max, 1);
- assert.strictEqual(h.exceeds, 0);
- assert.strictEqual(h.mean, 1);
- assert.strictEqual(h.stddev, 0);
+ strictEqual(h.min, 1);
+ strictEqual(h.minBigInt, 1n);
+ strictEqual(h.max, 1);
+ strictEqual(h.maxBigInt, 1n);
+ strictEqual(h.exceeds, 0);
+ strictEqual(h.mean, 1);
+ strictEqual(h.stddev, 0);
+
+ strictEqual(h.percentile(1), 1);
+ strictEqual(h.percentile(100), 1);
- assert.strictEqual(h.percentile(1), 1);
- assert.strictEqual(h.percentile(100), 1);
+ strictEqual(h.percentileBigInt(1), 1n);
+ strictEqual(h.percentileBigInt(100), 1n);
const mc = new MessageChannel();
mc.port1.onmessage = common.mustCall(({ data }) => {
- assert.strictEqual(h.min, 1);
- assert.strictEqual(h.max, 1);
- assert.strictEqual(h.exceeds, 0);
- assert.strictEqual(h.mean, 1);
- assert.strictEqual(h.stddev, 0);
+ strictEqual(h.min, 1);
+ strictEqual(h.max, 1);
+ strictEqual(h.exceeds, 0);
+ strictEqual(h.mean, 1);
+ strictEqual(h.stddev, 0);
data.record(2n);
data.recordDelta();
- assert.strictEqual(h.max, 2);
+ strictEqual(h.max, 2);
mc.port1.close();
});
@@ -57,13 +78,15 @@ const { inspect } = require('util');
{
const e = monitorEventLoopDelay();
+ strictEqual(e.count, 0);
e.enable();
const mc = new MessageChannel();
mc.port1.onmessage = common.mustCall(({ data }) => {
- assert(typeof data.min, 'number');
- assert(data.min > 0);
- assert.strictEqual(data.disable, undefined);
- assert.strictEqual(data.enable, undefined);
+ strictEqual(typeof data.min, 'number');
+ ok(data.min > 0);
+ ok(data.count > 0);
+ strictEqual(data.disable, undefined);
+ strictEqual(data.enable, undefined);
mc.port1.close();
});
setTimeout(() => mc.port2.postMessage(e), 100);
@@ -71,12 +94,66 @@ const { inspect } = require('util');
{
const h = createHistogram();
- assert(inspect(h, { depth: null }).startsWith('Histogram'));
- assert.strictEqual(inspect(h, { depth: -1 }), '[RecordableHistogram]');
+ ok(inspect(h, { depth: null }).startsWith('Histogram'));
+ strictEqual(inspect(h, { depth: -1 }), '[RecordableHistogram]');
}
{
// Tests that RecordableHistogram is impossible to construct manually
const h = createHistogram();
- assert.throws(() => new h.constructor(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
+ throws(() => new h.constructor(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
+}
+
+{
+ [
+ 'hello',
+ 1,
+ null,
+ ].forEach((i) => {
+ throws(() => createHistogram(i), { code: 'ERR_INVALID_ARG_TYPE' });
+ });
+
+ [
+ 'hello',
+ false,
+ null,
+ {},
+ ].forEach((i) => {
+ throws(() => createHistogram({ lowest: i }), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ });
+ throws(() => createHistogram({ highest: i }), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ });
+ throws(() => createHistogram({ figures: i }), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ });
+ });
+
+ createHistogram({ lowest: 1, highest: 11, figures: 1 });
+}
+
+{
+ const h1 = createHistogram();
+ const h2 = createHistogram();
+
+ h1.record(1);
+
+ strictEqual(h2.count, 0);
+ strictEqual(h1.count, 1);
+
+ h2.add(h1);
+
+ strictEqual(h2.count, 1);
+
+ [
+ 'hello',
+ 1,
+ false,
+ {},
+ ].forEach((i) => {
+ throws(() => h1.add(i), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ });
+ });
}