Skip to content

Commit fcf32cf

Browse files
anonrigaduh95
authored andcommitted
child_process: serialize advanced IPC messages natively
The `advanced` IPC serialization codec was implemented in JavaScript (ChildProcessSerializer / ChildProcessDeserializer in lib/internal/child_process/serialization.js). It allocated a wrapper serializer/deserializer per message and crossed the JS/C++ boundary several times for every message (writeHeader, writeValue, releaseBuffer, readHeader, readValue and friends). Move the codec into a native `ipc_serdes` binding that drives the V8 ValueSerializer/ValueDeserializer with a C++ delegate. The wire format is preserved byte-for-byte: a big-endian uint32 length prefix followed by the V8 payload, with ArrayBufferViews tagged as host objects so that Node Buffers round-trip as Buffers rather than plain Uint8Arrays. The JSON codec is left unchanged. A cctest (test/cctest/test_node_ipc_serdes.cc) exercises the binding directly, covering round-trips of primitives, objects, typed arrays and Buffers (including the Buffer-vs-Uint8Array distinction) and asserting the big-endian length-prefix framing. Round-trip throughput (benchmark/child_process/child-process-ipc-roundtrip): payload before after change 64 B ~300k/s ~800k/s +166% 1 KiB ~272k/s ~616k/s +126% 16 KiB ~91k/s ~120k/s +32% 64 KiB ~30k/s ~35k/s +16% The gain is largest for small messages, where per-message JavaScript overhead dominated, and tapers for large messages, where the actual serialization (already native) dominates. Signed-off-by: Yagiz Nizipli <yagiz@nizipli.com> PR-URL: #63933 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent d74b3a7 commit fcf32cf

9 files changed

Lines changed: 739 additions & 50 deletions

File tree

‎lib/internal/child_process/serialization.js‎

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,14 @@ const {
1010
}=primordials;
1111
const{ Buffer }=require('buffer');
1212
const{ StringDecoder }=require('string_decoder');
13-
constv8=require('v8');
14-
const{ isArrayBufferView }=require('internal/util/types');
15-
constassert=require('internal/assert');
13+
const{ serialize, deserialize }=internalBinding('ipc_serdes');
1614
const{ streamBaseState, kLastWriteWasAsync }=internalBinding('stream_wrap');
1715

1816
constkMessageBuffer=Symbol('kMessageBuffer');
1917
constkMessageBufferSize=Symbol('kMessageBufferSize');
2018
constkJSONBuffer=Symbol('kJSONBuffer');
2119
constkStringDecoder=Symbol('kStringDecoder');
2220

23-
// Extend V8's serializer APIs to give more JSON-like behaviour in
24-
// some cases; in particular, for native objects this serializes them the same
25-
// way that JSON does rather than throwing an exception.
26-
constkArrayBufferViewTag=0;
27-
constkNotArrayBufferViewTag=1;
28-
classChildProcessSerializerextendsv8.DefaultSerializer{
29-
_writeHostObject(object){
30-
if(isArrayBufferView(object)){
31-
this.writeUint32(kArrayBufferViewTag);
32-
returnsuper._writeHostObject(object);
33-
}
34-
this.writeUint32(kNotArrayBufferViewTag);
35-
this.writeValue({ ...object});
36-
}
37-
}
38-
39-
classChildProcessDeserializerextendsv8.DefaultDeserializer{
40-
_readHostObject(){
41-
consttag=this.readUint32();
42-
if(tag===kArrayBufferViewTag)
43-
returnsuper._readHostObject();
44-
45-
assert(tag===kNotArrayBufferViewTag);
46-
returnthis.readValue();
47-
}
48-
}
49-
5021
// Messages are parsed in either of the following formats:
5122
// - Newline-delimited JSON, or
5223
// - V8-serialized buffers, prefixed with their length as a big endian uint32
@@ -90,38 +61,25 @@ const advanced = {
9061
channel[kMessageBufferSize],
9162
);
9263

93-
constdeserializer=newChildProcessDeserializer(
94-
TypedArrayPrototypeSubarray(concatenatedBuffer,4,fullMessageSize),
95-
);
64+
constserializedMessage=
65+
TypedArrayPrototypeSubarray(concatenatedBuffer,4,fullMessageSize);
9666

9767
messageBufferHead=TypedArrayPrototypeSubarray(concatenatedBuffer,fullMessageSize);
9868
channel[kMessageBufferSize]=messageBufferHead.length;
9969
channel[kMessageBuffer]=
10070
channel[kMessageBufferSize]!==0 ? [messageBufferHead] : [];
10171

102-
deserializer.readHeader();
103-
yielddeserializer.readValue();
72+
yielddeserialize(serializedMessage);
10473
}
10574

10675
channel.buffering=channel[kMessageBufferSize]>0;
10776
},
10877

10978
writeChannelMessage(channel,req,message,handle){
110-
constser=newChildProcessSerializer();
111-
// Add 4 bytes, to later populate with message length
112-
ser.writeRawBytes(Buffer.allocUnsafe(4));
113-
ser.writeHeader();
114-
ser.writeValue(message);
115-
116-
constserializedMessage=ser.releaseBuffer();
117-
constserializedMessageLength=serializedMessage.length-4;
118-
119-
serializedMessage.set([
120-
serializedMessageLength>>24&0xFF,
121-
serializedMessageLength>>16&0xFF,
122-
serializedMessageLength>>8&0xFF,
123-
serializedMessageLength&0xFF,
124-
],0);
79+
// Pass the stable Buffer constructor so the native codec can classify Node
80+
// Buffers via `value.constructor === Buffer` without reading the tamperable
81+
// Buffer.prototype.constructor.
82+
constserializedMessage=serialize(message,Buffer);
12583

12684
constresult=channel.writeBuffer(req,serializedMessage,handle);
12785

‎node.gyp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
'src/node_http_parser.cc',
126126
'src/node_http2.cc',
127127
'src/node_i18n.cc',
128+
'src/node_ipc_serdes.cc',
128129
'src/node_locks.cc',
129130
'src/node_main_instance.cc',
130131
'src/node_messaging.cc',

‎src/node_binding.cc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
V(http_parser) \
6060
V(inspector) \
6161
V(internal_only_v8) \
62+
V(ipc_serdes) \
6263
V(js_stream) \
6364
V(js_udp_wrap) \
6465
V(locks) \

‎src/node_external_reference.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class ExternalReferenceRegistry {
8282
V(heap_utils) \
8383
V(http_parser) \
8484
V(internal_only_v8) \
85+
V(ipc_serdes) \
8586
V(locks) \
8687
V(messaging) \
8788
V(mksnapshot) \

0 commit comments

Comments
 (0)