Skip to content

Conversation

@anonrig
Copy link
Member

@anonrig anonrig commented Jul 21, 2022

I added 2 benchmarks to serialize and deserialize method in order to understand the performance impact of replacing a really big if statement with a switch statement. Here are my findings:

Serialize

Before

➜  node git:(perf/v8-deserialize-serialize) ./node-master benchmark/v8/serialize.js
v8/serialize.js n=1000000: 699,777.9223973043
➜  node git:(perf/v8-deserialize-serialize) ./node-master benchmark/v8/serialize.js
v8/serialize.js n=1000000: 696,074.8630130064
➜  node git:(perf/v8-deserialize-serialize) ./node-master benchmark/v8/serialize.js
v8/serialize.js n=1000000: 704,981.8835623765

After

➜  node git:(perf/v8-deserialize-serialize) ./node-debug benchmark/v8/serialize.js
v8/serialize.js n=1000000: 702,113.8598850783
➜  node git:(perf/v8-deserialize-serialize) ./node-debug benchmark/v8/serialize.js
v8/serialize.js n=1000000: 705,363.8894045315
➜  node git:(perf/v8-deserialize-serialize) ./node-debug benchmark/v8/serialize.js
v8/serialize.js n=1000000: 703,806.1453469822

Deserialize

Before

➜  node git:(perf/v8-deserialize-serialize) ./node-master benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,571,634.7166923403
➜  node git:(perf/v8-deserialize-serialize) ./node-master benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,567,321.4530283604
➜  node git:(perf/v8-deserialize-serialize) ./node-master benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,572,912.0436487873

After

➜  node git:(perf/v8-deserialize-serialize) ./node-debug benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,575,016.1488374511
➜  node git:(perf/v8-deserialize-serialize) ./node-debug benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,573,957.404044289
➜  node git:(perf/v8-deserialize-serialize) ./node-debug benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,579,010.319407197

@nodejs-github-bot nodejs-github-bot added the needs-ci PRs that need a full CI run. label Jul 21, 2022
@anonrig anonrig force-pushed the perf/v8-deserialize-serialize branch from aa6fd55 to 9579939 Compare July 21, 2022 22:01
@anonrig anonrig changed the title Add benchmarks to v8 serialize/deserialize and improve if statements v8: add benchmarks to serialize/deserialize and improve if statements Jul 21, 2022
lib/v8.js Outdated
if (index === 11) return BigInt64Array;
if (index === 12) return BigUint64Array;
return undefined;
switch (index) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using an array-based lookup table here? That would probably be even more efficient, especially for the last cases.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's faster, I'm updating my changes

➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-with-array ./benchmark/v8/serialize.js
v8/serialize.js n=1000000: 637,198.2984177626
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-perf-v8 ./benchmark/v8/serialize.js
v8/serialize.js n=1000000: 625,952.948029536
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-with-array ./benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,499,926.0363972564
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-perf-v8 ./benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,435,339.223199034

if (type === '[object BigUint64Array]') return 12;
return -1;

switch (type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this compare to an object or Map lookup?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object is slower compared to switch:

With switch:

➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-debug benchmark/v8/serialize.js
v8/serialize.js n=1000000: 705,193.2134097881
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-debug benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,595,554.4661464228

With object:

➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-v8-object benchmark/v8/serialize.js
v8/serialize.js n=1000000: 696,654.8116336825
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-v8-object benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,575,265.6004526608

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Map is faster on serialize but slower on deserialize:

With switch:

➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-master benchmark/v8/serialize.js
v8/serialize.js n=1000000: 690,649.3698025825
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-master benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,582,109.8185933107

With Map:

➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-v8-map benchmark/v8/serialize.js
v8/serialize.js n=1000000: 695,808.1758001995
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-v8-map benchmark/v8/serialize.js
v8/serialize.js n=1000000: 699,940.6800273677
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-v8-map benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,565,646.8890889874
➜  node git:(perf/v8-deserialize-serialize) ✗ ./node-v8-map benchmark/v8/deserialize.js
v8/deserialize.js n=1000000: 1,572,391.3241736297

Copy link
Contributor

@mscdex mscdex Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't make sense to me since the current serialize benchmark included in this PR doesn't even exercise this code path and for deserialize it'd never hit this code path because it's for serialization only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be noted when running benchmarks you can't just run them once and compare because the results can vary. So you either need to run them manually a bunch of times to get a feel for the expected range or use the compare.js script to do all of this for you and tell you whether results are statistically significant.

Copy link
Contributor

@mscdex mscdex Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the serialize benchmark to this, which does exercise the code path in question:

'use strict';

const common = require('../common.js');
const v8 = require('v8');

const bench = common.createBenchmark(main, {
  n: [1e6]
});

function main({ n }) {
  const abv = new BigUint64Array();
  bench.start();
  for (let i = 0; i < n; i++)
    v8.serialize({ a: 1, b: abv });
  bench.end(n);
}

Using that benchmark against a plain object, a plain null object, and a Map, all of them have either the same or less performance compared to the if/switch solution.

@anonrig anonrig force-pushed the perf/v8-deserialize-serialize branch from 9579939 to 74abb29 Compare July 29, 2022 12:54
@anonrig anonrig force-pushed the perf/v8-deserialize-serialize branch from 74abb29 to 33b9ce7 Compare July 29, 2022 13:07
});

function main({ n }) {
const serialized = v8.serialize({ a: 1 });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const serialized = v8.serialize({ a: 1 });
const serialized = v8.serialize({ a: 1, b: new BigUint64Array() });

Comment on lines +11 to +13
bench.start();
for (let i = 0; i < n; i++)
v8.serialize({ a: 1 });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bench.start();
for (let i = 0; i < n; i++)
v8.serialize({ a: 1 });
const typedArray = new BigUint64Array();
bench.start();
for (let i = 0; i < n; i++)
v8.serialize({ a: 1, b: typedArray });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants