Skip to content

Commit c74523f

Browse files
committed
src: implement util.types fast API calls
All util.types.is##Type() calls are ported to the fast API. This improves the performance for multiple APIs such as: - util.inspect (and util.format and console methods respectively) - util.isDeepStrictEqual - assert.(not)deepEqual (including strict and partial mode) It will also improve any other API where these APIs are used.a
1 parent 8456a12 commit c74523f

File tree

2 files changed

+111
-12
lines changed

2 files changed

+111
-12
lines changed

src/node_types.cc

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include "env-inl.h"
22
#include "node.h"
3+
#include "node_debug.h"
34
#include "node_external_reference.h"
45

6+
using v8::CFunction;
57
using v8::Context;
68
using v8::FunctionCallbackInfo;
79
using v8::Local;
@@ -36,22 +38,35 @@ namespace {
3638
V(DataView) \
3739
V(SharedArrayBuffer) \
3840
V(Proxy) \
39-
V(ModuleNamespaceObject) \
41+
V(ModuleNamespaceObject)
4042

43+
#define V(type) \
44+
static void Is##type(const FunctionCallbackInfo<Value>& args) { \
45+
args.GetReturnValue().Set(args[0]->Is##type()); \
46+
} \
47+
static bool Is##type##FastApi(Local<Value> unused, Local<Value> receiver) { \
48+
TRACK_V8_FAST_API_CALL("types.isMethod.macro"); \
49+
return receiver->Is##type(); \
50+
} \
51+
static CFunction fast_is_##type##_ = CFunction::Make(Is##type##FastApi);
4152

42-
#define V(type) \
43-
static void Is##type(const FunctionCallbackInfo<Value>& args) { \
44-
args.GetReturnValue().Set(args[0]->Is##type()); \
45-
}
46-
47-
VALUE_METHOD_MAP(V)
53+
VALUE_METHOD_MAP(V)
4854
#undef V
4955

5056
static void IsAnyArrayBuffer(const FunctionCallbackInfo<Value>& args) {
5157
args.GetReturnValue().Set(
5258
args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer());
5359
}
5460

61+
static bool IsAnyArrayBufferFastApi(Local<Value> unused,
62+
Local<Value> receiver) {
63+
TRACK_V8_FAST_API_CALL("types.isAnyArrayBuffer");
64+
return receiver->IsArrayBuffer() || receiver->IsSharedArrayBuffer();
65+
}
66+
67+
static CFunction fast_is_any_array_buffer_ =
68+
CFunction::Make(IsAnyArrayBufferFastApi);
69+
5570
static void IsBoxedPrimitive(const FunctionCallbackInfo<Value>& args) {
5671
args.GetReturnValue().Set(
5772
args[0]->IsNumberObject() ||
@@ -61,27 +76,57 @@ static void IsBoxedPrimitive(const FunctionCallbackInfo<Value>& args) {
6176
args[0]->IsSymbolObject());
6277
}
6378

79+
static bool IsBoxedPrimitiveFastApi(Local<Value> unused,
80+
Local<Value> receiver) {
81+
TRACK_V8_FAST_API_CALL("types.isBoxedPrimitive");
82+
return receiver->IsNumberObject() || receiver->IsStringObject() ||
83+
receiver->IsBooleanObject() || receiver->IsBigIntObject() ||
84+
receiver->IsSymbolObject();
85+
}
86+
87+
static CFunction fast_is_boxed_primitive_ =
88+
CFunction::Make(IsBoxedPrimitiveFastApi);
89+
6490
void InitializeTypes(Local<Object> target,
6591
Local<Value> unused,
6692
Local<Context> context,
6793
void* priv) {
68-
#define V(type) SetMethodNoSideEffect(context, target, "is" #type, Is##type);
94+
#define V(type) \
95+
SetFastMethodNoSideEffect( \
96+
context, target, "is" #type, Is##type, &fast_is_##type##_);
97+
6998
VALUE_METHOD_MAP(V)
7099
#undef V
71100

72-
SetMethodNoSideEffect(context, target, "isAnyArrayBuffer", IsAnyArrayBuffer);
73-
SetMethodNoSideEffect(context, target, "isBoxedPrimitive", IsBoxedPrimitive);
101+
SetFastMethodNoSideEffect(context,
102+
target,
103+
"isAnyArrayBuffer",
104+
IsAnyArrayBuffer,
105+
&fast_is_any_array_buffer_);
106+
SetFastMethodNoSideEffect(context,
107+
target,
108+
"isBoxedPrimitive",
109+
IsBoxedPrimitive,
110+
&fast_is_boxed_primitive_);
74111
}
75112

76113
} // anonymous namespace
77114

78115
void RegisterTypesExternalReferences(ExternalReferenceRegistry* registry) {
79-
#define V(type) registry->Register(Is##type);
116+
#define V(type) \
117+
registry->Register(Is##type); \
118+
registry->Register(Is##type##FastApi); \
119+
registry->Register(fast_is_##type##_.GetTypeInfo());
120+
80121
VALUE_METHOD_MAP(V)
81122
#undef V
82123

83124
registry->Register(IsAnyArrayBuffer);
125+
registry->Register(IsAnyArrayBufferFastApi);
126+
registry->Register(fast_is_any_array_buffer_.GetTypeInfo());
84127
registry->Register(IsBoxedPrimitive);
128+
registry->Register(IsBoxedPrimitiveFastApi);
129+
registry->Register(fast_is_boxed_primitive_.GetTypeInfo());
85130
}
86131
} // namespace node
87132

test/parallel/test-util-types.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --experimental-vm-modules --expose-internals
1+
// Flags: --experimental-vm-modules --expose-internals --allow-natives-syntax
22
'use strict';
33
const common = require('../common');
44
const assert = require('assert');
@@ -291,3 +291,57 @@ for (const [ value, _method ] of [
291291
assert.ok(!types.isCryptoKey());
292292
assert.ok(!types.isKeyObject());
293293
}
294+
295+
const values = [
296+
new String(),
297+
new Boolean(),
298+
new Number(),
299+
new Date(),
300+
new Error(),
301+
new RegExp(),
302+
new Map(),
303+
new Set(),
304+
new WeakMap(),
305+
new WeakSet(),
306+
new ArrayBuffer(),
307+
new SharedArrayBuffer(),
308+
new DataView(new ArrayBuffer()),
309+
];
310+
311+
function getValue() {
312+
return values[Math.floor(Math.random() * values.length)];
313+
}
314+
315+
for (const method of Object.values(types)) {
316+
// The function that will be optimized. It has to be a function written in
317+
// JavaScript, so the tested method is wrapped.
318+
function testFastPath(input) {
319+
return method(input);
320+
}
321+
322+
eval('%PrepareFunctionForOptimization(testFastPath)');
323+
// This call will let V8 know about the argument types that the function expects.
324+
testFastPath(getValue());
325+
326+
eval('%OptimizeFunctionOnNextCall(testFastPath)');
327+
for (let i = 0; i < 1000; i++) {
328+
// Trigger other code that also calls type methods
329+
inspect(getValue());
330+
// Call the function again to check that the fast path is taken.
331+
assert.strictEqual(typeof testFastPath(getValue()), 'boolean');
332+
}
333+
}
334+
335+
if (common.isDebug) {
336+
const { getV8FastApiCallCount } = internalBinding('debug');
337+
console.log(
338+
getV8FastApiCallCount(`types.isBoxedPrimitive`),
339+
getV8FastApiCallCount(`types.isMethod.macro`),
340+
getV8FastApiCallCount(`types.isAnyArrayBuffer`)
341+
);
342+
assert(
343+
getV8FastApiCallCount(`types.isBoxedPrimitive`) +
344+
getV8FastApiCallCount(`types.isMethod.macro`) +
345+
getV8FastApiCallCount(`types.isAnyArrayBuffer`) > 1
346+
);
347+
}

0 commit comments

Comments
 (0)