Skip to content

Change default value of ipc to advanced #784

@ehmicky

Description

@ehmicky

I have done some small research on the difference between the json and the advanced serialization formats of the ipc feature. I just wanted to share them for future reference, so will close this issue.

Speed

Generally speaking, json is ~1.3x faster on small values, while advanced is up to 6x faster faster on big values. This aligns with what the Node.js PR that introduced this feature is benchmarking too. Which is a bit more nuanced than what the documentation is mentioning.

Additionally, performance may not be equivalent to that of JSON, depending on the structure of the passed data. Therefore, this feature requires opting in by setting the serialization option to 'advanced' when calling child_process.spawn() or child_process.fork().

Using my machine (Node 21, Ubuntu 23.10), advanced is:

  • 1.3x slower: on boolean, number, null, short string, empty array, any array with non-random items, empty object
  • same speed: 1KB string, array with 50 random items, object with 10_000 random fields
  • 1.7x faster: object with 1_000_000 random fields
  • 2.5x faster: array with 1_000_000 random items
  • 6x faster: 1MB string

Benchmark code:

import {spawn} from 'node:child_process'
import {once} from 'node:events'

const loopCount = 1e3
const value = '.'.repeat(1e6)

for (const serialization of ['json', 'advanced']) {
  const childProcess = spawn('node', ['child.js'], {stdio: ['pipe', 'pipe', 'pipe', 'ipc'], serialization});
  await once(childProcess, 'message')

  console.time(serialization)
  for (let i = 0; i < loopCount; i += 1) {
    childProcess.send(value)
    await once(childProcess, 'message')
  }
  console.timeEnd(serialization)

  childProcess.disconnect()
}

Compatibility

The documentation mentions that:

However, this format is not a full superset of JSON, and e.g. properties set on objects of such built-in types will not be passed on through the serialization step.

I have tried many values of different types. The result is this:

  • advanced keeps regExps, Map, Set, ArrayBuffer, ArrayBufferView, Buffer and Uint8Array as is. Those values are serialized to empty objects with json
  • advanced keeps Date instances as is. Those are serialized to strings with json.
  • advanced supports undefined and bigint, except at the top-level. Those values throw with json.
  • advanced keeps undefined object properties as is. Those properties are filtered out with json.
  • advanced supports NaN, Infinity. Also undefined array items. Those values are serialized to null with json.
  • advanced supports circular references. Those values throw with json.
  • advanced has a generic (not full) support for Error instances. Those values are serialized to empty objects with json.
  • advanced throws if an object/array contains functions or symbols. Those properties are filtered out (but do not error) with json.
  • both formats throw on symbols, functions, promises, proxies, Intl.*
  • both formats ignore symbol keys, setters, getters, property descriptors, inherited properties and non-enumerable properties in objects

So I would personally consider advanced to be mostly a superset of json, which the following caveats:

  • Objects/arrays containing functions, symbols or promises throw with advanced, while they are ignored with json.
  • Objects with toJSON() throw, while they are handled by json
  • Dates are kept as is instead of being converted to strings

I wanted to share this information for any future discussion around that feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions