From 81b3e5dcfc2fabedfb9a0939e4b4d66c86e9c40a Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 11 Jan 2017 01:38:59 +0800 Subject: [PATCH] benchmark: improve WHATWG URL benchmarks * add benchmark to compare the performance of getting url properties between the WHATWG URL and the legacy implementation * add benchmark to compare the performance of serializing urls between the WHATWG URL and the legacy implementation * refactor the benchmark for comparing parsing performance between the two implementations PR-URL: https://github.com/nodejs/node/pull/10678 Reviewed-By: James M Snell Reviewed-By: Brian White --- .../url/legacy-vs-whatwg-url-get-prop.js | 109 ++++++++++++++++++ benchmark/url/legacy-vs-whatwg-url-parse.js | 69 +++++++++++ .../url/legacy-vs-whatwg-url-serialize.js | 71 ++++++++++++ benchmark/url/new-url-parse.js | 57 --------- 4 files changed, 249 insertions(+), 57 deletions(-) create mode 100644 benchmark/url/legacy-vs-whatwg-url-get-prop.js create mode 100644 benchmark/url/legacy-vs-whatwg-url-parse.js create mode 100644 benchmark/url/legacy-vs-whatwg-url-serialize.js delete mode 100644 benchmark/url/new-url-parse.js diff --git a/benchmark/url/legacy-vs-whatwg-url-get-prop.js b/benchmark/url/legacy-vs-whatwg-url-get-prop.js new file mode 100644 index 00000000000000..f703b75b16f6f7 --- /dev/null +++ b/benchmark/url/legacy-vs-whatwg-url-get-prop.js @@ -0,0 +1,109 @@ +'use strict'; +const common = require('../common.js'); +const url = require('url'); +const URL = url.URL; +const assert = require('assert'); + +const inputs = { + long: 'http://nodejs.org:89/docs/latest/api/url.html#test?' + + 'payload1=true&payload2=false&test=1&benchmark=3&' + + 'foo=38.38.011.293&bar=1234834910480&test=19299&3992&' + + 'key=f5c65e1e98fe07e648249ad41e1cfdb0', + short: 'https://nodejs.org/en/blog/', + idn: 'http://你好你好', + auth: 'https://user:pass@example.com/path?search=1', + special: 'file:///foo/bar/test/node.js', + percent: 'https://%E4%BD%A0/foo', + dot: 'https://example.org/./a/../b/./c' +}; + +const bench = common.createBenchmark(main, { + type: Object.keys(inputs), + method: ['legacy', 'whatwg'], + n: [1e5] +}); + +// At the time of writing, when using a passed property name to index +// the object, Crankshaft would generate a LoadKeyedGeneric even when it +// remains a constant in the function, so here we must use the literal +// instead to get a LoadNamedField. +function useLegacy(n, input) { + var obj = url.parse(input); + var noDead = { + protocol: obj.protocol, + auth: obj.auth, + host: obj.host, + hostname: obj.hostname, + port: obj.port, + pathname: obj.pathname, + search: obj.search, + hash: obj.hash + }; + // It's necessary to assign the values to an object + // to avoid loop invariant code motion. + bench.start(); + for (var i = 0; i < n; i += 1) { + noDead.protocol = obj.protocol; + noDead.auth = obj.auth; + noDead.host = obj.host; + noDead.hostname = obj.hostname; + noDead.port = obj.port; + noDead.pathname = obj.pathname; + noDead.search = obj.search; + noDead.hash = obj.hash; + } + bench.end(n); + return noDead; +} + +function useWHATWG(n, input) { + var obj = new URL(input); + var noDead = { + protocol: obj.protocol, + auth: obj.username + ':' + obj.password, + host: obj.host, + hostname: obj.hostname, + port: obj.port, + pathname: obj.pathname, + search: obj.search, + hash: obj.hash + }; + bench.start(); + for (var i = 0; i < n; i += 1) { + noDead.protocol = obj.protocol; + noDead.auth = obj.username + ':' + obj.password; + noDead.host = obj.host; + noDead.hostname = obj.hostname; + noDead.port = obj.port; + noDead.pathname = obj.pathname; + noDead.search = obj.search; + noDead.hash = obj.hash; + } + bench.end(n); + return noDead; +} + +function main(conf) { + const type = conf.type; + const n = conf.n | 0; + const method = conf.method; + + const input = inputs[type]; + if (!input) { + throw new Error('Unknown input type'); + } + + var noDead; // Avoid dead code elimination. + switch (method) { + case 'legacy': + noDead = useLegacy(n, input); + break; + case 'whatwg': + noDead = useWHATWG(n, input); + break; + default: + throw new Error('Unknown method'); + } + + assert.ok(noDead); +} diff --git a/benchmark/url/legacy-vs-whatwg-url-parse.js b/benchmark/url/legacy-vs-whatwg-url-parse.js new file mode 100644 index 00000000000000..ca7a48466c7dc3 --- /dev/null +++ b/benchmark/url/legacy-vs-whatwg-url-parse.js @@ -0,0 +1,69 @@ +'use strict'; +const common = require('../common.js'); +const url = require('url'); +const URL = url.URL; +const assert = require('assert'); + +const inputs = { + long: 'http://nodejs.org:89/docs/latest/api/url.html#test?' + + 'payload1=true&payload2=false&test=1&benchmark=3&' + + 'foo=38.38.011.293&bar=1234834910480&test=19299&3992&' + + 'key=f5c65e1e98fe07e648249ad41e1cfdb0', + short: 'https://nodejs.org/en/blog/', + idn: 'http://你好你好', + auth: 'https://user:pass@example.com/path?search=1', + special: 'file:///foo/bar/test/node.js', + percent: 'https://%E4%BD%A0/foo', + dot: 'https://example.org/./a/../b/./c' +}; + +const bench = common.createBenchmark(main, { + type: Object.keys(inputs), + method: ['legacy', 'whatwg'], + n: [1e5] +}); + +function useLegacy(n, input) { + var noDead = url.parse(input); + bench.start(); + for (var i = 0; i < n; i += 1) { + noDead = url.parse(input); + } + bench.end(n); + return noDead; +} + +function useWHATWG(n, input) { + var noDead = url.parse(input); + bench.start(); + for (var i = 0; i < n; i += 1) { + noDead = new URL(input); + } + bench.end(n); + return noDead; +} + +function main(conf) { + const type = conf.type; + const n = conf.n | 0; + const method = conf.method; + + const input = inputs[type]; + if (!input) { + throw new Error('Unknown input type'); + } + + var noDead; // Avoid dead code elimination. + switch (method) { + case 'legacy': + noDead = useLegacy(n, input); + break; + case 'whatwg': + noDead = useWHATWG(n, input); + break; + default: + throw new Error('Unknown method'); + } + + assert.ok(noDead); +} diff --git a/benchmark/url/legacy-vs-whatwg-url-serialize.js b/benchmark/url/legacy-vs-whatwg-url-serialize.js new file mode 100644 index 00000000000000..c0b7f5a6ce1565 --- /dev/null +++ b/benchmark/url/legacy-vs-whatwg-url-serialize.js @@ -0,0 +1,71 @@ +'use strict'; +const common = require('../common.js'); +const url = require('url'); +const URL = url.URL; +const assert = require('assert'); + +const inputs = { + long: 'http://nodejs.org:89/docs/latest/api/url.html#test?' + + 'payload1=true&payload2=false&test=1&benchmark=3&' + + 'foo=38.38.011.293&bar=1234834910480&test=19299&3992&' + + 'key=f5c65e1e98fe07e648249ad41e1cfdb0', + short: 'https://nodejs.org/en/blog/', + idn: 'http://你好你好', + auth: 'https://user:pass@example.com/path?search=1', + special: 'file:///foo/bar/test/node.js', + percent: 'https://%E4%BD%A0/foo', + dot: 'https://example.org/./a/../b/./c' +}; + +const bench = common.createBenchmark(main, { + type: Object.keys(inputs), + method: ['legacy', 'whatwg'], + n: [1e5] +}); + +function useLegacy(n, input, prop) { + var obj = url.parse(input); + var noDead = url.format(obj); + bench.start(); + for (var i = 0; i < n; i += 1) { + noDead = url.format(obj); + } + bench.end(n); + return noDead; +} + +function useWHATWG(n, input, prop) { + var obj = new URL(input); + var noDead = obj.toString(); + bench.start(); + for (var i = 0; i < n; i += 1) { + noDead = obj.toString(); + } + bench.end(n); + return noDead; +} + +function main(conf) { + const type = conf.type; + const n = conf.n | 0; + const method = conf.method; + + const input = inputs[type]; + if (!input) { + throw new Error('Unknown input type'); + } + + var noDead; // Avoid dead code elimination. + switch (method) { + case 'legacy': + noDead = useLegacy(n, input); + break; + case 'whatwg': + noDead = useWHATWG(n, input); + break; + default: + throw new Error('Unknown method'); + } + + assert.ok(noDead); +} diff --git a/benchmark/url/new-url-parse.js b/benchmark/url/new-url-parse.js deleted file mode 100644 index ef60e81847682b..00000000000000 --- a/benchmark/url/new-url-parse.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; -const common = require('../common.js'); -const url = require('url'); -const v8 = require('v8'); - -const bench = common.createBenchmark(main, { - type: 'one two three four five'.split(' '), - method: ['old', 'new'], - n: [25e4] -}); - -function useOld(n, input) { - // Force-optimize url.parse() so that the benchmark doesn't get - // disrupted by the optimizer kicking in halfway through. - url.parse(input); - v8.setFlagsFromString('--allow_natives_syntax'); - eval('%OptimizeFunctionOnNextCall(url.parse)'); - - bench.start(); - for (var i = 0; i < n; i += 1) - url.parse(input); - bench.end(n); -} - -function useNew(n, input) { - bench.start(); - for (var i = 0; i < n; i += 1) - new url.URL(input); - bench.end(n); -} - -function main(conf) { - const type = conf.type; - const n = conf.n | 0; - const method = conf.method; - - var inputs = { - one: 'http://nodejs.org/docs/latest/api/url.html#url_url_format_urlobj', - two: 'http://blog.nodejs.org/', - three: 'https://encrypted.google.com/search?q=url&q=site:npmjs.org&hl=en', - four: 'javascript:alert("node is awesome");', - //five: 'some.ran/dom/url.thing?oh=yes#whoo', - five: 'https://user:pass@example.com/', - }; - var input = inputs[type] || ''; - - switch (method) { - case 'old': - useOld(n, input); - break; - case 'new': - useNew(n, input); - break; - default: - throw new Error('Unknown method'); - } -}