mirror of https://github.com/nodejs/node.git
src: speed up process.getActiveResourcesInfo()
This change reduces the number of calls that were crossing the JS-C++ boundary to 1 and also removes the need for calling Array::New() multiple times internally and ArrayPrototypeConcat-ing the results later on, thus improving performance. Refs: https://github.com/nodejs/node/pull/44445#pullrequestreview-1220052837 Signed-off-by: Darshan Sen <raisinten@gmail.com> PR-URL: https://github.com/nodejs/node/pull/46014 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
parent
6d49f46b48
commit
e35e893d26
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
const { createBenchmark } = require('../common.js');
|
||||
|
||||
const { connect, createServer } = require('net');
|
||||
const { open } = require('fs');
|
||||
|
||||
const bench = createBenchmark(main, {
|
||||
handlesCount: [1e4],
|
||||
requestsCount: [1e4],
|
||||
timeoutsCount: [1e4],
|
||||
immediatesCount: [1e4],
|
||||
n: [1e5],
|
||||
});
|
||||
|
||||
function main({ handlesCount, requestsCount, timeoutsCount, immediatesCount, n }) {
|
||||
const server = createServer().listen();
|
||||
const clients = [];
|
||||
for (let i = 0; i < handlesCount; i++) {
|
||||
clients.push(connect({ port: server.address().port }));
|
||||
}
|
||||
|
||||
for (let i = 0; i < requestsCount; i++) {
|
||||
open(__filename, 'r', () => {});
|
||||
}
|
||||
|
||||
for (let i = 0; i < timeoutsCount; ++i) {
|
||||
setTimeout(() => {}, 1);
|
||||
}
|
||||
|
||||
for (let i = 0; i < immediatesCount; ++i) {
|
||||
setImmediate(() => {});
|
||||
}
|
||||
|
||||
bench.start();
|
||||
for (let i = 0; i < n; ++i) {
|
||||
process.getActiveResourcesInfo();
|
||||
}
|
||||
bench.end(n);
|
||||
|
||||
for (const client of clients) {
|
||||
client.destroy();
|
||||
}
|
||||
server.close();
|
||||
}
|
|
@ -55,9 +55,6 @@
|
|||
setupPrepareStackTrace();
|
||||
|
||||
const {
|
||||
Array,
|
||||
ArrayPrototypeFill,
|
||||
ArrayPrototypePushApply,
|
||||
FunctionPrototypeCall,
|
||||
JSONParse,
|
||||
Number,
|
||||
|
@ -171,15 +168,7 @@ const rawMethods = internalBinding('process_methods');
|
|||
// TODO(joyeecheung): either remove them or make them public
|
||||
process._getActiveRequests = rawMethods._getActiveRequests;
|
||||
process._getActiveHandles = rawMethods._getActiveHandles;
|
||||
|
||||
process.getActiveResourcesInfo = function() {
|
||||
const timerCounts = internalTimers.getTimerCounts();
|
||||
const info = rawMethods._getActiveRequestsInfo();
|
||||
ArrayPrototypePushApply(info, rawMethods._getActiveHandlesInfo());
|
||||
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.timeoutCount), 'Timeout'));
|
||||
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.immediateCount), 'Immediate'));
|
||||
return info;
|
||||
};
|
||||
process.getActiveResourcesInfo = rawMethods.getActiveResourcesInfo;
|
||||
|
||||
// TODO(joyeecheung): remove these
|
||||
process.reallyExit = rawMethods.reallyExit;
|
||||
|
|
|
@ -87,6 +87,7 @@ const {
|
|||
toggleTimerRef,
|
||||
getLibuvNow,
|
||||
immediateInfo,
|
||||
timeoutInfo,
|
||||
toggleImmediateRef
|
||||
} = internalBinding('timers');
|
||||
|
||||
|
@ -137,7 +138,11 @@ let timerListId = NumberMIN_SAFE_INTEGER;
|
|||
const kRefed = Symbol('refed');
|
||||
|
||||
let nextExpiry = Infinity;
|
||||
let refCount = 0;
|
||||
// timeoutInfo is an Int32Array that contains the reference count of Timeout
|
||||
// objects at index 0. This is a TypedArray so that GetActiveResourcesInfo() in
|
||||
// `src/node_process_methods.cc` is able to access this value without crossing
|
||||
// the JS-C++ boundary, which is slow at the time of writing.
|
||||
timeoutInfo[0] = 0;
|
||||
|
||||
// This is a priority queue with a custom sorting function that first compares
|
||||
// the expiry times of two lists and if they're the same then compares their
|
||||
|
@ -302,12 +307,12 @@ class ImmediateList {
|
|||
const immediateQueue = new ImmediateList();
|
||||
|
||||
function incRefCount() {
|
||||
if (refCount++ === 0)
|
||||
if (timeoutInfo[0]++ === 0)
|
||||
toggleTimerRef(true);
|
||||
}
|
||||
|
||||
function decRefCount() {
|
||||
if (--refCount === 0)
|
||||
if (--timeoutInfo[0] === 0)
|
||||
toggleTimerRef(false);
|
||||
}
|
||||
|
||||
|
@ -498,7 +503,7 @@ function getTimerCallbacks(runNextTicks) {
|
|||
while ((list = timerListQueue.peek()) != null) {
|
||||
if (list.expiry > now) {
|
||||
nextExpiry = list.expiry;
|
||||
return refCount > 0 ? nextExpiry : -nextExpiry;
|
||||
return timeoutInfo[0] > 0 ? nextExpiry : -nextExpiry;
|
||||
}
|
||||
if (ranAtLeastOneList)
|
||||
runNextTicks();
|
||||
|
@ -544,7 +549,7 @@ function getTimerCallbacks(runNextTicks) {
|
|||
timer._destroyed = true;
|
||||
|
||||
if (timer[kRefed])
|
||||
refCount--;
|
||||
timeoutInfo[0]--;
|
||||
|
||||
if (destroyHooksExist())
|
||||
emitDestroy(asyncId);
|
||||
|
@ -572,7 +577,7 @@ function getTimerCallbacks(runNextTicks) {
|
|||
timer._destroyed = true;
|
||||
|
||||
if (timer[kRefed])
|
||||
refCount--;
|
||||
timeoutInfo[0]--;
|
||||
|
||||
if (destroyHooksExist())
|
||||
emitDestroy(asyncId);
|
||||
|
@ -643,13 +648,6 @@ class Immediate {
|
|||
}
|
||||
}
|
||||
|
||||
function getTimerCounts() {
|
||||
return {
|
||||
timeoutCount: refCount,
|
||||
immediateCount: immediateInfo[kRefCount],
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
TIMEOUT_MAX,
|
||||
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
|
||||
|
@ -676,5 +674,4 @@ module.exports = {
|
|||
timerListQueue,
|
||||
decRefCount,
|
||||
incRefCount,
|
||||
getTimerCounts,
|
||||
};
|
||||
|
|
|
@ -323,6 +323,10 @@ inline ImmediateInfo* Environment::immediate_info() {
|
|||
return &immediate_info_;
|
||||
}
|
||||
|
||||
inline AliasedInt32Array& Environment::timeout_info() {
|
||||
return timeout_info_;
|
||||
}
|
||||
|
||||
inline TickInfo* Environment::tick_info() {
|
||||
return &tick_info_;
|
||||
}
|
||||
|
|
|
@ -654,6 +654,7 @@ Environment::Environment(IsolateData* isolate_data,
|
|||
isolate_data_(isolate_data),
|
||||
async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
|
||||
immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)),
|
||||
timeout_info_(isolate_, 1, MAYBE_FIELD_PTR(env_info, timeout_info)),
|
||||
tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)),
|
||||
timer_base_(uv_now(isolate_data->event_loop())),
|
||||
exec_argv_(exec_args),
|
||||
|
@ -1594,6 +1595,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
|
|||
|
||||
info.async_hooks = async_hooks_.Serialize(ctx, creator);
|
||||
info.immediate_info = immediate_info_.Serialize(ctx, creator);
|
||||
info.timeout_info = timeout_info_.Serialize(ctx, creator);
|
||||
info.tick_info = tick_info_.Serialize(ctx, creator);
|
||||
info.performance_state = performance_state_->Serialize(ctx, creator);
|
||||
info.exit_info = exit_info_.Serialize(ctx, creator);
|
||||
|
@ -1639,6 +1641,7 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
|
|||
|
||||
async_hooks_.Deserialize(ctx);
|
||||
immediate_info_.Deserialize(ctx);
|
||||
timeout_info_.Deserialize(ctx);
|
||||
tick_info_.Deserialize(ctx);
|
||||
performance_state_->Deserialize(ctx);
|
||||
exit_info_.Deserialize(ctx);
|
||||
|
@ -1836,6 +1839,7 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
|
|||
tracker->TrackField("cleanup_queue", cleanup_queue_);
|
||||
tracker->TrackField("async_hooks", async_hooks_);
|
||||
tracker->TrackField("immediate_info", immediate_info_);
|
||||
tracker->TrackField("timeout_info", timeout_info_);
|
||||
tracker->TrackField("tick_info", tick_info_);
|
||||
tracker->TrackField("principal_realm", principal_realm_);
|
||||
|
||||
|
|
|
@ -465,6 +465,7 @@ struct EnvSerializeInfo {
|
|||
AsyncHooks::SerializeInfo async_hooks;
|
||||
TickInfo::SerializeInfo tick_info;
|
||||
ImmediateInfo::SerializeInfo immediate_info;
|
||||
AliasedBufferIndex timeout_info;
|
||||
performance::PerformanceState::SerializeInfo performance_state;
|
||||
AliasedBufferIndex exit_info;
|
||||
AliasedBufferIndex stream_base_state;
|
||||
|
@ -676,6 +677,7 @@ class Environment : public MemoryRetainer {
|
|||
|
||||
inline AsyncHooks* async_hooks();
|
||||
inline ImmediateInfo* immediate_info();
|
||||
inline AliasedInt32Array& timeout_info();
|
||||
inline TickInfo* tick_info();
|
||||
inline uint64_t timer_base() const;
|
||||
inline std::shared_ptr<KVStore> env_vars();
|
||||
|
@ -1007,6 +1009,7 @@ class Environment : public MemoryRetainer {
|
|||
|
||||
AsyncHooks async_hooks_;
|
||||
ImmediateInfo immediate_info_;
|
||||
AliasedInt32Array timeout_info_;
|
||||
TickInfo tick_info_;
|
||||
const uint64_t timer_base_;
|
||||
std::shared_ptr<KVStore> env_vars_;
|
||||
|
|
|
@ -259,21 +259,6 @@ static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
|
|||
Array::New(env->isolate(), request_v.data(), request_v.size()));
|
||||
}
|
||||
|
||||
static void GetActiveRequestsInfo(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
std::vector<Local<Value>> requests_info;
|
||||
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
|
||||
AsyncWrap* w = req_wrap->GetAsyncWrap();
|
||||
if (w->persistent().IsEmpty()) continue;
|
||||
requests_info.emplace_back(OneByteString(env->isolate(),
|
||||
w->MemoryInfoName().c_str()));
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(
|
||||
Array::New(env->isolate(), requests_info.data(), requests_info.size()));
|
||||
}
|
||||
|
||||
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
|
||||
// implemented here for consistency with GetActiveRequests().
|
||||
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
|
||||
|
@ -289,18 +274,37 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
|
|||
Array::New(env->isolate(), handle_v.data(), handle_v.size()));
|
||||
}
|
||||
|
||||
void GetActiveHandlesInfo(const FunctionCallbackInfo<Value>& args) {
|
||||
static void GetActiveResourcesInfo(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
std::vector<Local<Value>> resources_info;
|
||||
|
||||
std::vector<Local<Value>> handles_info;
|
||||
for (HandleWrap* w : *env->handle_wrap_queue()) {
|
||||
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue;
|
||||
handles_info.emplace_back(OneByteString(env->isolate(),
|
||||
w->MemoryInfoName().c_str()));
|
||||
// Active requests
|
||||
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
|
||||
AsyncWrap* w = req_wrap->GetAsyncWrap();
|
||||
if (w->persistent().IsEmpty()) continue;
|
||||
resources_info.emplace_back(
|
||||
OneByteString(env->isolate(), w->MemoryInfoName().c_str()));
|
||||
}
|
||||
|
||||
// Active handles
|
||||
for (HandleWrap* w : *env->handle_wrap_queue()) {
|
||||
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue;
|
||||
resources_info.emplace_back(
|
||||
OneByteString(env->isolate(), w->MemoryInfoName().c_str()));
|
||||
}
|
||||
|
||||
// Active timeouts
|
||||
resources_info.insert(resources_info.end(),
|
||||
env->timeout_info()[0],
|
||||
OneByteString(env->isolate(), "Timeout"));
|
||||
|
||||
// Active immediates
|
||||
resources_info.insert(resources_info.end(),
|
||||
env->immediate_info()->ref_count(),
|
||||
OneByteString(env->isolate(), "Immediate"));
|
||||
|
||||
args.GetReturnValue().Set(
|
||||
Array::New(env->isolate(), handles_info.data(), handles_info.size()));
|
||||
Array::New(env->isolate(), resources_info.data(), resources_info.size()));
|
||||
}
|
||||
|
||||
static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {
|
||||
|
@ -583,10 +587,9 @@ static void Initialize(Local<Object> target,
|
|||
SetMethod(context, target, "resourceUsage", ResourceUsage);
|
||||
|
||||
SetMethod(context, target, "_debugEnd", DebugEnd);
|
||||
SetMethod(context, target, "_getActiveRequestsInfo", GetActiveRequestsInfo);
|
||||
SetMethod(context, target, "_getActiveRequests", GetActiveRequests);
|
||||
SetMethod(context, target, "_getActiveHandles", GetActiveHandles);
|
||||
SetMethod(context, target, "_getActiveHandlesInfo", GetActiveHandlesInfo);
|
||||
SetMethod(context, target, "getActiveResourcesInfo", GetActiveResourcesInfo);
|
||||
SetMethod(context, target, "_kill", Kill);
|
||||
SetMethod(context, target, "_rawDebug", RawDebug);
|
||||
|
||||
|
@ -614,9 +617,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
|||
registry->Register(ResourceUsage);
|
||||
|
||||
registry->Register(GetActiveRequests);
|
||||
registry->Register(GetActiveRequestsInfo);
|
||||
registry->Register(GetActiveHandles);
|
||||
registry->Register(GetActiveHandlesInfo);
|
||||
registry->Register(GetActiveResourcesInfo);
|
||||
registry->Register(Kill);
|
||||
|
||||
registry->Register(Cwd);
|
||||
|
|
|
@ -120,6 +120,7 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
|
|||
<< "// -- async_hooks ends --\n"
|
||||
<< i.tick_info << ", // tick_info\n"
|
||||
<< i.immediate_info << ", // immediate_info\n"
|
||||
<< i.timeout_info << ", // timeout_info\n"
|
||||
<< "// -- performance_state begins --\n"
|
||||
<< i.performance_state << ",\n"
|
||||
<< "// -- performance_state ends --\n"
|
||||
|
@ -735,6 +736,7 @@ EnvSerializeInfo FileReader::Read() {
|
|||
result.async_hooks = Read<AsyncHooks::SerializeInfo>();
|
||||
result.tick_info = Read<TickInfo::SerializeInfo>();
|
||||
result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
|
||||
result.timeout_info = Read<AliasedBufferIndex>();
|
||||
result.performance_state =
|
||||
Read<performance::PerformanceState::SerializeInfo>();
|
||||
result.exit_info = Read<AliasedBufferIndex>();
|
||||
|
@ -755,6 +757,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
|
|||
size_t written_total = Write<AsyncHooks::SerializeInfo>(data.async_hooks);
|
||||
written_total += Write<TickInfo::SerializeInfo>(data.tick_info);
|
||||
written_total += Write<ImmediateInfo::SerializeInfo>(data.immediate_info);
|
||||
written_total += Write<AliasedBufferIndex>(data.timeout_info);
|
||||
written_total += Write<performance::PerformanceState::SerializeInfo>(
|
||||
data.performance_state);
|
||||
written_total += Write<AliasedBufferIndex>(data.exit_info);
|
||||
|
|
|
@ -59,6 +59,12 @@ void Initialize(Local<Object> target,
|
|||
FIXED_ONE_BYTE_STRING(env->isolate(), "immediateInfo"),
|
||||
env->immediate_info()->fields().GetJSArray())
|
||||
.Check();
|
||||
|
||||
target
|
||||
->Set(context,
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "timeoutInfo"),
|
||||
env->timeout_info().GetJSArray())
|
||||
.Check();
|
||||
}
|
||||
} // anonymous namespace
|
||||
void RegisterTimerExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
|
|
Loading…
Reference in New Issue