Skip to content

Commit a936eae

Browse files
RaisinTenRafaelGSS
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 <[email protected]> PR-URL: #46014 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent 38dd506 commit a936eae

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,
@@ -159,15 +156,7 @@ const rawMethods = internalBinding('process_methods');
159156
// TODO(joyeecheung): either remove them or make them public
160157
process._getActiveRequests = rawMethods._getActiveRequests;
161158
process._getActiveHandles = rawMethods._getActiveHandles;
162-
163-
process.getActiveResourcesInfo = function() {
164-
const timerCounts = internalTimers.getTimerCounts();
165-
const info = rawMethods._getActiveRequestsInfo();
166-
ArrayPrototypePushApply(info, rawMethods._getActiveHandlesInfo());
167-
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.timeoutCount), 'Timeout'));
168-
ArrayPrototypePushApply(info, ArrayPrototypeFill(new Array(timerCounts.immediateCount), 'Immediate'));
169-
return info;
170-
};
159+
process.getActiveResourcesInfo = rawMethods.getActiveResourcesInfo;
171160

172161
// TODO(joyeecheung): remove these
173162
process.reallyExit = rawMethods.reallyExit;

lib/internal/timers.js

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

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

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

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

303308
function incRefCount() {
304-
if (refCount++ === 0)
309+
if (timeoutInfo[0]++ === 0)
305310
toggleTimerRef(true);
306311
}
307312

308313
function decRefCount() {
309-
if (--refCount === 0)
314+
if (--timeoutInfo[0] === 0)
310315
toggleTimerRef(false);
311316
}
312317

@@ -497,7 +502,7 @@ function getTimerCallbacks(runNextTicks) {
497502
while ((list = timerListQueue.peek()) != null) {
498503
if (list.expiry > now) {
499504
nextExpiry = list.expiry;
500-
return refCount > 0 ? nextExpiry : -nextExpiry;
505+
return timeoutInfo[0] > 0 ? nextExpiry : -nextExpiry;
501506
}
502507
if (ranAtLeastOneList)
503508
runNextTicks();
@@ -543,7 +548,7 @@ function getTimerCallbacks(runNextTicks) {
543548
timer._destroyed = true;
544549

545550
if (timer[kRefed])
546-
refCount--;
551+
timeoutInfo[0]--;
547552

548553
if (destroyHooksExist())
549554
emitDestroy(asyncId);
@@ -571,7 +576,7 @@ function getTimerCallbacks(runNextTicks) {
571576
timer._destroyed = true;
572577

573578
if (timer[kRefed])
574-
refCount--;
579+
timeoutInfo[0]--;
575580

576581
if (destroyHooksExist())
577582
emitDestroy(asyncId);
@@ -642,13 +647,6 @@ class Immediate {
642647
}
643648
}
644649

645-
function getTimerCounts() {
646-
return {
647-
timeoutCount: refCount,
648-
immediateCount: immediateInfo[kRefCount],
649-
};
650-
}
651-
652650
module.exports = {
653651
TIMEOUT_MAX,
654652
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
@@ -675,5 +673,4 @@ module.exports = {
675673
timerListQueue,
676674
decRefCount,
677675
incRefCount,
678-
getTimerCounts,
679676
};

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
@@ -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),
@@ -1593,6 +1594,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
15931594

15941595
info.async_hooks = async_hooks_.Serialize(ctx, creator);
15951596
info.immediate_info = immediate_info_.Serialize(ctx, creator);
1597+
info.timeout_info = timeout_info_.Serialize(ctx, creator);
15961598
info.tick_info = tick_info_.Serialize(ctx, creator);
15971599
info.performance_state = performance_state_->Serialize(ctx, creator);
15981600
info.exiting = exiting_.Serialize(ctx, creator);
@@ -1638,6 +1640,7 @@ void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
16381640

16391641
async_hooks_.Deserialize(ctx);
16401642
immediate_info_.Deserialize(ctx);
1643+
timeout_info_.Deserialize(ctx);
16411644
tick_info_.Deserialize(ctx);
16421645
performance_state_->Deserialize(ctx);
16431646
exiting_.Deserialize(ctx);
@@ -1835,6 +1838,7 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
18351838
tracker->TrackField("cleanup_queue", cleanup_queue_);
18361839
tracker->TrackField("async_hooks", async_hooks_);
18371840
tracker->TrackField("immediate_info", immediate_info_);
1841+
tracker->TrackField("timeout_info", timeout_info_);
18381842
tracker->TrackField("tick_info", tick_info_);
18391843
tracker->TrackField("principal_realm", principal_realm_);
18401844

src/env.h

+3
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 exiting;
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();
@@ -997,6 +999,7 @@ class Environment : public MemoryRetainer {
997999

9981000
AsyncHooks async_hooks_;
9991001
ImmediateInfo immediate_info_;
1002+
AliasedInt32Array timeout_info_;
10001003
TickInfo tick_info_;
10011004
const uint64_t timer_base_;
10021005
std::shared_ptr<KVStore> env_vars_;

src/node_process_methods.cc

+26-24
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

+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"
@@ -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.exiting = 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.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)