Skip to content

Commit 41f5a29

Browse files
RaisinTenjuanarbol
authored andcommitted
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: #44445 (review) Signed-off-by: Darshan Sen <raisinten@gmail.com> PR-URL: #46014 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 8de6425 commit 41f5a29

File tree

9 files changed

+103
-50
lines changed

9 files changed

+103
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
const { createBenchmark } = require('../common.js');
4+
5+
const { connect, createServer } = require('net');
6+
const { open } = require('fs');
7+
8+
const bench = createBenchmark(main, {
9+
handlesCount: [1e4],
10+
requestsCount: [1e4],
11+
timeoutsCount: [1e4],
12+
immediatesCount: [1e4],
13+
n: [1e5],
14+
});
15+
16+
function main({ handlesCount, requestsCount, timeoutsCount, immediatesCount, n }) {
17+
const server = createServer().listen();
18+
const clients = [];
19+
for (let i = 0; i < handlesCount; i++) {
20+
clients.push(connect({ port: server.address().port }));
21+
}
22+
23+
for (let i = 0; i < requestsCount; i++) {
24+
open(__filename, 'r', () => {});
25+
}
26+
27+
for (let i = 0; i < timeoutsCount; ++i) {
28+
setTimeout(() => {}, 1);
29+
}
30+
31+
for (let i = 0; i < immediatesCount; ++i) {
32+
setImmediate(() => {});
33+
}
34+
35+
bench.start();
36+
for (let i = 0; i < n; ++i) {
37+
process.getActiveResourcesInfo();
38+
}
39+
bench.end(n);
40+
41+
for (const client of clients) {
42+
client.destroy();
43+
}
44+
server.close();
45+
}

lib/internal/bootstrap/node.js

+1-12
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@
5555
setupPrepareStackTrace();
5656

5757
const {
58-
Array,
59-
ArrayPrototypeFill,
60-
ArrayPrototypePushApply,
6158
FunctionPrototypeCall,
6259
JSONParse,
6360
ObjectDefineProperty,
@@ -192,15 +189,7 @@ const rawMethods = internalBinding('process_methods');
192189
// TODO(joyeecheung): either remove them or make them public
193190
process._getActiveRequests = rawMethods._getActiveRequests;
194191
process._getActiveHandles = rawMethods._getActiveHandles;
195-
196-
process.getActiveResourcesInfo = function() {
197-
const timerCounts = internalTimers.getTimerCounts();
198-
const info = rawMethods._getActiveRequestsInfo();
199-
ArrayPrototypePushApply(info, rawMethods._getActiveHandlesInfo());
200-
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.timeoutCount), 'Timeout'));
201-
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.immediateCount), 'Immediate'));
202-
return info;
203-
};
192+
process.getActiveResourcesInfo = rawMethods.getActiveResourcesInfo;
204193

205194
// TODO(joyeecheung): remove these
206195
process.reallyExit = rawMethods.reallyExit;

lib/internal/timers.js

+11-14
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ const {
8787
toggleTimerRef,
8888
getLibuvNow,
8989
immediateInfo,
90+
timeoutInfo,
9091
toggleImmediateRef
9192
} = internalBinding('timers');
9293

@@ -137,7 +138,11 @@ let timerListId = NumberMIN_SAFE_INTEGER;
137138
const kRefed = Symbol('refed');
138139

139140
let nextExpiry = Infinity;
140-
let refCount = 0;
141+
// timeoutInfo is an Int32Array that contains the reference count of Timeout
142+
// objects at index 0. This is a TypedArray so that GetActiveResourcesInfo() in
143+
// `src/node_process_methods.cc` is able to access this value without crossing
144+
// the JS-C++ boundary, which is slow at the time of writing.
145+
timeoutInfo[0] = 0;
141146

142147
// This is a priority queue with a custom sorting function that first compares
143148
// the expiry times of two lists and if they're the same then compares their
@@ -302,12 +307,12 @@ class ImmediateList {
302307
const immediateQueue = new ImmediateList();
303308

304309
function incRefCount() {
305-
if (refCount++ === 0)
310+
if (timeoutInfo[0]++ === 0)
306311
toggleTimerRef(true);
307312
}
308313

309314
function decRefCount() {
310-
if (--refCount === 0)
315+
if (--timeoutInfo[0] === 0)
311316
toggleTimerRef(false);
312317
}
313318

@@ -498,7 +503,7 @@ function getTimerCallbacks(runNextTicks) {
498503
while ((list = timerListQueue.peek()) != null) {
499504
if (list.expiry > now) {
500505
nextExpiry = list.expiry;
501-
return refCount > 0 ? nextExpiry : -nextExpiry;
506+
return timeoutInfo[0] > 0 ? nextExpiry : -nextExpiry;
502507
}
503508
if (ranAtLeastOneList)
504509
runNextTicks();
@@ -544,7 +549,7 @@ function getTimerCallbacks(runNextTicks) {
544549
timer._destroyed = true;
545550

546551
if (timer[kRefed])
547-
refCount--;
552+
timeoutInfo[0]--;
548553

549554
if (destroyHooksExist())
550555
emitDestroy(asyncId);
@@ -572,7 +577,7 @@ function getTimerCallbacks(runNextTicks) {
572577
timer._destroyed = true;
573578

574579
if (timer[kRefed])
575-
refCount--;
580+
timeoutInfo[0]--;
576581

577582
if (destroyHooksExist())
578583
emitDestroy(asyncId);
@@ -643,13 +648,6 @@ class Immediate {
643648
}
644649
}
645650

646-
function getTimerCounts() {
647-
return {
648-
timeoutCount: refCount,
649-
immediateCount: immediateInfo[kRefCount],
650-
};
651-
}
652-
653651
module.exports = {
654652
TIMEOUT_MAX,
655653
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
@@ -676,5 +674,4 @@ module.exports = {
676674
timerListQueue,
677675
decRefCount,
678676
incRefCount,
679-
getTimerCounts,
680677
};

src/env-inl.h

+4
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ inline ImmediateInfo* Environment::immediate_info() {
323323
return &immediate_info_;
324324
}
325325

326+
inline AliasedInt32Array& Environment::timeout_info() {
327+
return timeout_info_;
328+
}
329+
326330
inline TickInfo* Environment::tick_info() {
327331
return &tick_info_;
328332
}

src/env.cc

+4
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ Environment::Environment(IsolateData* isolate_data,
649649
isolate_data_(isolate_data),
650650
async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
651651
immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)),
652+
timeout_info_(isolate_, 1, MAYBE_FIELD_PTR(env_info, timeout_info)),
652653
tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)),
653654
timer_base_(uv_now(isolate_data->event_loop())),
654655
exec_argv_(exec_args),
@@ -1603,6 +1604,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
16031604

16041605
info.async_hooks = async_hooks_.Serialize(ctx, creator);
16051606
info.immediate_info = immediate_info_.Serialize(ctx, creator);
1607+
info.timeout_info = timeout_info_.Serialize(ctx, creator);
16061608
info.tick_info = tick_info_.Serialize(ctx, creator);
16071609
info.performance_state = performance_state_->Serialize(ctx, creator);
16081610
info.exiting = exiting_.Serialize(ctx, creator);
@@ -1649,6 +1651,7 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
16491651
builtins_in_snapshot = info->builtins;
16501652
async_hooks_.Deserialize(ctx);
16511653
immediate_info_.Deserialize(ctx);
1654+
timeout_info_.Deserialize(ctx);
16521655
tick_info_.Deserialize(ctx);
16531656
performance_state_->Deserialize(ctx);
16541657
exiting_.Deserialize(ctx);
@@ -1845,6 +1848,7 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
18451848
tracker->TrackField("cleanup_queue", cleanup_queue_);
18461849
tracker->TrackField("async_hooks", async_hooks_);
18471850
tracker->TrackField("immediate_info", immediate_info_);
1851+
tracker->TrackField("timeout_info", timeout_info_);
18481852
tracker->TrackField("tick_info", tick_info_);
18491853
tracker->TrackField("principal_realm", principal_realm_);
18501854

src/env.h

+3
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ struct EnvSerializeInfo {
460460
AsyncHooks::SerializeInfo async_hooks;
461461
TickInfo::SerializeInfo tick_info;
462462
ImmediateInfo::SerializeInfo immediate_info;
463+
AliasedBufferIndex timeout_info;
463464
performance::PerformanceState::SerializeInfo performance_state;
464465
AliasedBufferIndex exiting;
465466
AliasedBufferIndex stream_base_state;
@@ -667,6 +668,7 @@ class Environment : public MemoryRetainer {
667668

668669
inline AsyncHooks* async_hooks();
669670
inline ImmediateInfo* immediate_info();
671+
inline AliasedInt32Array& timeout_info();
670672
inline TickInfo* tick_info();
671673
inline uint64_t timer_base() const;
672674
inline std::shared_ptr<KVStore> env_vars();
@@ -988,6 +990,7 @@ class Environment : public MemoryRetainer {
988990

989991
AsyncHooks async_hooks_;
990992
ImmediateInfo immediate_info_;
993+
AliasedInt32Array timeout_info_;
991994
TickInfo tick_info_;
992995
const uint64_t timer_base_;
993996
std::shared_ptr<KVStore> env_vars_;

src/node_process_methods.cc

+26-24
Original file line numberDiff line numberDiff line change
@@ -258,21 +258,6 @@ static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
258258
Array::New(env->isolate(), request_v.data(), request_v.size()));
259259
}
260260

261-
static void GetActiveRequestsInfo(const FunctionCallbackInfo<Value>& args) {
262-
Environment* env = Environment::GetCurrent(args);
263-
264-
std::vector<Local<Value>> requests_info;
265-
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
266-
AsyncWrap* w = req_wrap->GetAsyncWrap();
267-
if (w->persistent().IsEmpty()) continue;
268-
requests_info.emplace_back(OneByteString(env->isolate(),
269-
w->MemoryInfoName().c_str()));
270-
}
271-
272-
args.GetReturnValue().Set(
273-
Array::New(env->isolate(), requests_info.data(), requests_info.size()));
274-
}
275-
276261
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
277262
// implemented here for consistency with GetActiveRequests().
278263
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
@@ -288,18 +273,37 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
288273
Array::New(env->isolate(), handle_v.data(), handle_v.size()));
289274
}
290275

291-
void GetActiveHandlesInfo(const FunctionCallbackInfo<Value>& args) {
276+
static void GetActiveResourcesInfo(const FunctionCallbackInfo<Value>& args) {
292277
Environment* env = Environment::GetCurrent(args);
278+
std::vector<Local<Value>> resources_info;
279+
280+
// Active requests
281+
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
282+
AsyncWrap* w = req_wrap->GetAsyncWrap();
283+
if (w->persistent().IsEmpty()) continue;
284+
resources_info.emplace_back(
285+
OneByteString(env->isolate(), w->MemoryInfoName().c_str()));
286+
}
293287

294-
std::vector<Local<Value>> handles_info;
288+
// Active handles
295289
for (HandleWrap* w : *env->handle_wrap_queue()) {
296290
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue;
297-
handles_info.emplace_back(OneByteString(env->isolate(),
298-
w->MemoryInfoName().c_str()));
291+
resources_info.emplace_back(
292+
OneByteString(env->isolate(), w->MemoryInfoName().c_str()));
299293
}
300294

295+
// Active timeouts
296+
resources_info.insert(resources_info.end(),
297+
env->timeout_info()[0],
298+
OneByteString(env->isolate(), "Timeout"));
299+
300+
// Active immediates
301+
resources_info.insert(resources_info.end(),
302+
env->immediate_info()->ref_count(),
303+
OneByteString(env->isolate(), "Immediate"));
304+
301305
args.GetReturnValue().Set(
302-
Array::New(env->isolate(), handles_info.data(), handles_info.size()));
306+
Array::New(env->isolate(), resources_info.data(), resources_info.size()));
303307
}
304308

305309
static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {
@@ -578,10 +582,9 @@ static void Initialize(Local<Object> target,
578582
SetMethod(context, target, "resourceUsage", ResourceUsage);
579583

580584
SetMethod(context, target, "_debugEnd", DebugEnd);
581-
SetMethod(context, target, "_getActiveRequestsInfo", GetActiveRequestsInfo);
582585
SetMethod(context, target, "_getActiveRequests", GetActiveRequests);
583586
SetMethod(context, target, "_getActiveHandles", GetActiveHandles);
584-
SetMethod(context, target, "_getActiveHandlesInfo", GetActiveHandlesInfo);
587+
SetMethod(context, target, "getActiveResourcesInfo", GetActiveResourcesInfo);
585588
SetMethod(context, target, "_kill", Kill);
586589
SetMethod(context, target, "_rawDebug", RawDebug);
587590

@@ -609,9 +612,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
609612
registry->Register(ResourceUsage);
610613

611614
registry->Register(GetActiveRequests);
612-
registry->Register(GetActiveRequestsInfo);
613615
registry->Register(GetActiveHandles);
614-
registry->Register(GetActiveHandlesInfo);
616+
registry->Register(GetActiveResourcesInfo);
615617
registry->Register(Kill);
616618

617619
registry->Register(Cwd);

src/node_snapshotable.cc

+3
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
120120
<< "// -- async_hooks ends --\n"
121121
<< i.tick_info << ", // tick_info\n"
122122
<< i.immediate_info << ", // immediate_info\n"
123+
<< i.timeout_info << ", // timeout_info\n"
123124
<< "// -- performance_state begins --\n"
124125
<< i.performance_state << ",\n"
125126
<< "// -- performance_state ends --\n"
@@ -734,6 +735,7 @@ EnvSerializeInfo FileReader::Read() {
734735
result.async_hooks = Read<AsyncHooks::SerializeInfo>();
735736
result.tick_info = Read<TickInfo::SerializeInfo>();
736737
result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
738+
result.timeout_info = Read<AliasedBufferIndex>();
737739
result.performance_state =
738740
Read<performance::PerformanceState::SerializeInfo>();
739741
result.exiting = Read<AliasedBufferIndex>();
@@ -755,6 +757,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
755757
written_total += Write<AsyncHooks::SerializeInfo>(data.async_hooks);
756758
written_total += Write<TickInfo::SerializeInfo>(data.tick_info);
757759
written_total += Write<ImmediateInfo::SerializeInfo>(data.immediate_info);
760+
written_total += Write<AliasedBufferIndex>(data.timeout_info);
758761
written_total += Write<performance::PerformanceState::SerializeInfo>(
759762
data.performance_state);
760763
written_total += Write<AliasedBufferIndex>(data.exiting);

src/timers.cc

+6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ void Initialize(Local<Object> target,
5959
FIXED_ONE_BYTE_STRING(env->isolate(), "immediateInfo"),
6060
env->immediate_info()->fields().GetJSArray())
6161
.Check();
62+
63+
target
64+
->Set(context,
65+
FIXED_ONE_BYTE_STRING(env->isolate(), "timeoutInfo"),
66+
env->timeout_info().GetJSArray())
67+
.Check();
6268
}
6369
} // anonymous namespace
6470
void RegisterTimerExternalReferences(ExternalReferenceRegistry* registry) {

0 commit comments

Comments
 (0)