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

perf,src: add HistogramBase and internal/histogram.js #31988

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
94 changes: 94 additions & 0 deletions lib/internal/histogram.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use strict';

const {
customInspectSymbol: kInspect,
} = require('internal/util');

const { format } = require('util');
const { Map, Symbol } = primordials;

const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
} = require('internal/errors').codes;

const kDestroy = Symbol('kDestroy');
const kHandle = Symbol('kHandle');

// Histograms are created internally by Node.js and used to
// record various metrics. This Histogram class provides a
// generally read-only view of the internal histogram.
class Histogram {
#handle = undefined;
#map = new Map();

constructor(internal) {
this.#handle = internal;
}

[kInspect]() {
const obj = {
min: this.min,
max: this.max,
mean: this.mean,
exceeds: this.exceeds,
stddev: this.stddev,
percentiles: this.percentiles,
};
return `Histogram ${format(obj)}`;
}

get min() {
return this.#handle ? this.#handle.min() : undefined;
}

get max() {
return this.#handle ? this.#handle.max() : undefined;
}

get mean() {
return this.#handle ? this.#handle.mean() : undefined;
}

get exceeds() {
return this.#handle ? this.#handle.exceeds() : undefined;
}

get stddev() {
return this.#handle ? this.#handle.stddev() : undefined;
}

percentile(percentile) {
if (typeof percentile !== 'number')
throw new ERR_INVALID_ARG_TYPE('percentile', 'number', percentile);

if (percentile <= 0 || percentile > 100)
throw new ERR_INVALID_ARG_VALUE.RangeError('percentile', percentile);

return this.#handle ? this.#handle.percentile(percentile) : undefined;
}

get percentiles() {
this.#map.clear();
if (this.#handle)
this.#handle.percentiles(this.#map);
return this.#map;
}

reset() {
if (this.#handle)
this.#handle.reset();
}

[kDestroy]() {
this.#handle = undefined;
}

get [kHandle]() { return this.#handle; }
}

module.exports = {
Histogram,
kDestroy,
kHandle,
};
49 changes: 6 additions & 43 deletions lib/perf_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const {
ArrayIsArray,
Boolean,
Map,
NumberIsSafeInteger,
ObjectDefineProperties,
ObjectDefineProperty,
Expand Down Expand Up @@ -52,16 +51,18 @@ const kInspect = require('internal/util').customInspectSymbol;

const {
ERR_INVALID_CALLBACK,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_OPT_VALUE,
ERR_VALID_PERFORMANCE_ENTRY_TYPE,
ERR_INVALID_PERFORMANCE_MARK
} = require('internal/errors').codes;

const {
Histogram,
kHandle,
} = require('internal/histogram');

const { setImmediate } = require('timers');
const kHandle = Symbol('handle');
const kMap = Symbol('map');
const kCallback = Symbol('callback');
const kTypes = Symbol('types');
const kEntries = Symbol('entries');
Expand Down Expand Up @@ -557,47 +558,9 @@ function sortedInsert(list, entry) {
list.splice(location, 0, entry);
}

class ELDHistogram {
constructor(handle) {
this[kHandle] = handle;
this[kMap] = new Map();
}

reset() { this[kHandle].reset(); }
class ELDHistogram extends Histogram {
enable() { return this[kHandle].enable(); }
disable() { return this[kHandle].disable(); }

get exceeds() { return this[kHandle].exceeds(); }
get min() { return this[kHandle].min(); }
get max() { return this[kHandle].max(); }
get mean() { return this[kHandle].mean(); }
get stddev() { return this[kHandle].stddev(); }
percentile(percentile) {
if (typeof percentile !== 'number') {
throw new ERR_INVALID_ARG_TYPE('percentile', 'number', percentile);
}
if (percentile <= 0 || percentile > 100) {
throw new ERR_INVALID_ARG_VALUE.RangeError('percentile',
percentile);
}
return this[kHandle].percentile(percentile);
}
get percentiles() {
this[kMap].clear();
this[kHandle].percentiles(this[kMap]);
return this[kMap];
}

[kInspect]() {
return {
min: this.min,
max: this.max,
mean: this.mean,
stddev: this.stddev,
percentiles: this.percentiles,
exceeds: this.exceeds
};
}
}

function monitorEventLoopDelay(options = {}) {
Expand Down
2 changes: 2 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
'lib/internal/fs/watchers.js',
'lib/internal/http.js',
'lib/internal/heap_utils.js',
'lib/internal/histogram.js',
'lib/internal/idna.js',
'lib/internal/inspector_async_hook.js',
'lib/internal/js_stream_socket.js',
Expand Down Expand Up @@ -534,6 +535,7 @@
'src/fs_event_wrap.cc',
'src/handle_wrap.cc',
'src/heap_utils.cc',
'src/histogram.cc',
'src/js_native_api.h',
'src/js_native_api_types.h',
'src/js_native_api_v8.cc',
Expand Down
1 change: 1 addition & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ constexpr size_t kFsStatsBufferLength =
V(filehandlereadwrap_template, v8::ObjectTemplate) \
V(fsreqpromise_constructor_template, v8::ObjectTemplate) \
V(handle_wrap_ctor_template, v8::FunctionTemplate) \
V(histogram_instance_template, v8::ObjectTemplate) \
V(http2settings_constructor_template, v8::ObjectTemplate) \
V(http2stream_constructor_template, v8::ObjectTemplate) \
V(http2ping_constructor_template, v8::ObjectTemplate) \
Expand Down
70 changes: 45 additions & 25 deletions src/histogram-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,78 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "histogram.h"
#include "base_object-inl.h"
#include "node_internals.h"

namespace node {

inline Histogram::Histogram(int64_t lowest, int64_t highest, int figures) {
CHECK_EQ(0, hdr_init(lowest, highest, figures, &histogram_));
void Histogram::Reset() {
hdr_reset(histogram_.get());
}

inline Histogram::~Histogram() {
hdr_close(histogram_);
bool Histogram::Record(int64_t value) {
return hdr_record_value(histogram_.get(), value);
}

inline void Histogram::Reset() {
hdr_reset(histogram_);
int64_t Histogram::Min() {
return hdr_min(histogram_.get());
}

inline bool Histogram::Record(int64_t value) {
return hdr_record_value(histogram_, value);
int64_t Histogram::Max() {
return hdr_max(histogram_.get());
}

inline int64_t Histogram::Min() {
return hdr_min(histogram_);
double Histogram::Mean() {
return hdr_mean(histogram_.get());
}

inline int64_t Histogram::Max() {
return hdr_max(histogram_);
double Histogram::Stddev() {
return hdr_stddev(histogram_.get());
}

inline double Histogram::Mean() {
return hdr_mean(histogram_);
}

inline double Histogram::Stddev() {
return hdr_stddev(histogram_);
}

inline double Histogram::Percentile(double percentile) {
double Histogram::Percentile(double percentile) {
CHECK_GT(percentile, 0);
CHECK_LE(percentile, 100);
return hdr_value_at_percentile(histogram_, percentile);
return static_cast<double>(
hdr_value_at_percentile(histogram_.get(), percentile));
}

inline void Histogram::Percentiles(std::function<void(double, double)> fn) {
template <typename Iterator>
void Histogram::Percentiles(Iterator&& fn) {
hdr_iter iter;
hdr_iter_percentile_init(&iter, histogram_, 1);
hdr_iter_percentile_init(&iter, histogram_.get(), 1);
while (hdr_iter_next(&iter)) {
double key = iter.specifics.percentiles.percentile;
double value = iter.value;
double value = static_cast<double>(iter.value);
fn(key, value);
}
}

bool HistogramBase::RecordDelta() {
uint64_t time = uv_hrtime();
bool ret = true;
if (prev_ > 0) {
int64_t delta = time - prev_;
if (delta > 0) {
ret = Record(delta);
TraceDelta(delta);
if (!ret) {
if (exceeds_ < 0xFFFFFFFF)
exceeds_++;
TraceExceeds(delta);
}
}
}
prev_ = time;
return ret;
}

void HistogramBase::ResetState() {
Reset();
exceeds_ = 0;
prev_ = 0;
}

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
Expand Down
Loading