Skip to content

Commit e35e893

Browse files
authored
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 6d49f46 commit e35e893

File tree

9 files changed

+103
-50
lines changed

9 files changed

+103
-50
lines changed
Lines changed: 45 additions & 0 deletions
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

Lines changed: 1 addition & 12 deletions
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
Number,
@@ -171,15 +168,7 @@ const rawMethods = internalBinding('process_methods');
171168
// TODO(joyeecheung): either remove them or make them public
172169
process._getActiveRequests = rawMethods._getActiveRequests;
173170
process._getActiveHandles = rawMethods._getActiveHandles;
174-
175-
process.getActiveResourcesInfo = function() {
176-
const timerCounts = internalTimers.getTimerCounts();
177-
const info = rawMethods._getActiveRequestsInfo();
178-
ArrayPrototypePushApply(info, rawMethods._getActiveHandlesInfo());
179-
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.timeoutCount), 'Timeout'));
180-
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.immediateCount), 'Immediate'));
181-
return info;
182-
};
171+
process.getActiveResourcesInfo = rawMethods.getActiveResourcesInfo;
183172

184173
// TODO(joyeecheung): remove these
185174
process.reallyExit = rawMethods.reallyExit;

lib/internal/timers.js

Lines changed: 11 additions & 14 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ Environment::Environment(IsolateData* isolate_data,
654654
isolate_data_(isolate_data),
655655
async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
656656
immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)),
657+
timeout_info_(isolate_, 1, MAYBE_FIELD_PTR(env_info, timeout_info)),
657658
tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)),
658659
timer_base_(uv_now(isolate_data->event_loop())),
659660
exec_argv_(exec_args),
@@ -1594,6 +1595,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
15941595

15951596
info.async_hooks = async_hooks_.Serialize(ctx, creator);
15961597
info.immediate_info = immediate_info_.Serialize(ctx, creator);
1598+
info.timeout_info = timeout_info_.Serialize(ctx, creator);
15971599
info.tick_info = tick_info_.Serialize(ctx, creator);
15981600
info.performance_state = performance_state_->Serialize(ctx, creator);
15991601
info.exit_info = exit_info_.Serialize(ctx, creator);
@@ -1639,6 +1641,7 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
16391641

16401642
async_hooks_.Deserialize(ctx);
16411643
immediate_info_.Deserialize(ctx);
1644+
timeout_info_.Deserialize(ctx);
16421645
tick_info_.Deserialize(ctx);
16431646
performance_state_->Deserialize(ctx);
16441647
exit_info_.Deserialize(ctx);
@@ -1836,6 +1839,7 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
18361839
tracker->TrackField("cleanup_queue", cleanup_queue_);
18371840
tracker->TrackField("async_hooks", async_hooks_);
18381841
tracker->TrackField("immediate_info", immediate_info_);
1842+
tracker->TrackField("timeout_info", timeout_info_);
18391843
tracker->TrackField("tick_info", tick_info_);
18401844
tracker->TrackField("principal_realm", principal_realm_);
18411845

src/env.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ struct EnvSerializeInfo {
465465
AsyncHooks::SerializeInfo async_hooks;
466466
TickInfo::SerializeInfo tick_info;
467467
ImmediateInfo::SerializeInfo immediate_info;
468+
AliasedBufferIndex timeout_info;
468469
performance::PerformanceState::SerializeInfo performance_state;
469470
AliasedBufferIndex exit_info;
470471
AliasedBufferIndex stream_base_state;
@@ -676,6 +677,7 @@ class Environment : public MemoryRetainer {
676677

677678
inline AsyncHooks* async_hooks();
678679
inline ImmediateInfo* immediate_info();
680+
inline AliasedInt32Array& timeout_info();
679681
inline TickInfo* tick_info();
680682
inline uint64_t timer_base() const;
681683
inline std::shared_ptr<KVStore> env_vars();
@@ -1007,6 +1009,7 @@ class Environment : public MemoryRetainer {
10071009

10081010
AsyncHooks async_hooks_;
10091011
ImmediateInfo immediate_info_;
1012+
AliasedInt32Array timeout_info_;
10101013
TickInfo tick_info_;
10111014
const uint64_t timer_base_;
10121015
std::shared_ptr<KVStore> env_vars_;

src/node_process_methods.cc

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

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

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

295-
std::vector<Local<Value>> handles_info;
289+
// Active handles
296290
for (HandleWrap* w : *env->handle_wrap_queue()) {
297291
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue;
298-
handles_info.emplace_back(OneByteString(env->isolate(),
299-
w->MemoryInfoName().c_str()));
292+
resources_info.emplace_back(
293+
OneByteString(env->isolate(), w->MemoryInfoName().c_str()));
300294
}
301295

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

306310
static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {
@@ -583,10 +587,9 @@ static void Initialize(Local<Object> target,
583587
SetMethod(context, target, "resourceUsage", ResourceUsage);
584588

585589
SetMethod(context, target, "_debugEnd", DebugEnd);
586-
SetMethod(context, target, "_getActiveRequestsInfo", GetActiveRequestsInfo);
587590
SetMethod(context, target, "_getActiveRequests", GetActiveRequests);
588591
SetMethod(context, target, "_getActiveHandles", GetActiveHandles);
589-
SetMethod(context, target, "_getActiveHandlesInfo", GetActiveHandlesInfo);
592+
SetMethod(context, target, "getActiveResourcesInfo", GetActiveResourcesInfo);
590593
SetMethod(context, target, "_kill", Kill);
591594
SetMethod(context, target, "_rawDebug", RawDebug);
592595

@@ -614,9 +617,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
614617
registry->Register(ResourceUsage);
615618

616619
registry->Register(GetActiveRequests);
617-
registry->Register(GetActiveRequestsInfo);
618620
registry->Register(GetActiveHandles);
619-
registry->Register(GetActiveHandlesInfo);
621+
registry->Register(GetActiveResourcesInfo);
620622
registry->Register(Kill);
621623

622624
registry->Register(Cwd);

src/node_snapshotable.cc

Lines changed: 3 additions & 0 deletions
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"
@@ -735,6 +736,7 @@ EnvSerializeInfo FileReader::Read() {
735736
result.async_hooks = Read<AsyncHooks::SerializeInfo>();
736737
result.tick_info = Read<TickInfo::SerializeInfo>();
737738
result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
739+
result.timeout_info = Read<AliasedBufferIndex>();
738740
result.performance_state =
739741
Read<performance::PerformanceState::SerializeInfo>();
740742
result.exit_info = Read<AliasedBufferIndex>();
@@ -755,6 +757,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
755757
size_t 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.exit_info);

src/timers.cc

Lines changed: 6 additions & 0 deletions
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)