Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

benchmark: reduce string concatenations #12455

Closed
wants to merge 4 commits into from
Closed

benchmark: reduce string concatenations #12455

wants to merge 4 commits into from

Conversation

vsemozhetbyt
Copy link
Contributor

Checklist
Affected core subsystem(s)

benchmark

I. Presupposition

String concatenations usually increase syntax noise and add confusion (a reader often has to check if string concatenation or number addition is intended). Moreover, it seems this method is not optimal performance-wise.

II. Cases

This PR aims two cases of string concatenation:

  • multipart concatenations with variable interpolation ('a' + b + 'c');
  • type coercion to String ('' + notString). This approach, while idiomatic, is not most declarative.

III. Performance

The added benchmark compares these variants:

  • 'a' + b + 'c' vs ['a', b, 'c'].join('') vs `a${b}c`
  • String(notString) vs '' + notString vs `${notString}`

Results with Node.js 8.0.0 rc:

es\string-concatenations.js mode="multi-concat"       n=1000:   909,455.5180758832
es\string-concatenations.js mode="multi-join"         n=1000:   290,896.9895943238
es\string-concatenations.js mode="multi-template"     n=1000: 2,620,236.0832711025

es\string-concatenations.js mode="to-string-string"   n=1000: 6,020,179.642160523
es\string-concatenations.js mode="to-string-concat"   n=1000: 1,141,656.7723079734
es\string-concatenations.js mode="to-string-template" n=1000: 5,700,376.794906143

IV. Commits

  1. Reduce string concatenations: replace concatenations with template literals, rewrap,
    String.prototype.repeat(), String(). Some replacements do not add indisputable readability gain, but I tried to be consistent.

  2. Add a benchmark for string concatenations vs counterparts.

  3. This commit is added here to avoid PR race condition and merge conflicts. It fixes an URL in _http-benchmarkers.js (removes duplicate hash symbol).

  4. This commit is added here to avoid PR race condition and merge conflicts. It fixes an evident typo in _http-benchmarkers.js: Error() constructor has only one parameter, while callback() has 5 (compare a previous call and callback() signature).

All changed files were minimally tested. CI for linting and new tests concerning benchmarks will be launched.

@vsemozhetbyt vsemozhetbyt added the benchmark Issues and PRs related to the benchmark subsystem. label Apr 17, 2017
@nodejs-github-bot nodejs-github-bot added assert Issues and PRs related to the assert subsystem. benchmark Issues and PRs related to the benchmark subsystem. buffer Issues and PRs related to the buffer subsystem. crypto Issues and PRs related to the crypto subsystem. fs Issues and PRs related to the fs subsystem / file system. http Issues or PRs related to the http subsystem. module Issues and PRs related to the module subsystem. net Issues and PRs related to the net subsystem. v8 engine Issues and PRs related to the V8 dependency. labels Apr 17, 2017
@vsemozhetbyt vsemozhetbyt removed assert Issues and PRs related to the assert subsystem. buffer Issues and PRs related to the buffer subsystem. crypto Issues and PRs related to the crypto subsystem. fs Issues and PRs related to the fs subsystem / file system. http Issues or PRs related to the http subsystem. module Issues and PRs related to the module subsystem. net Issues and PRs related to the net subsystem. v8 engine Issues and PRs related to the V8 dependency. labels Apr 17, 2017
@vsemozhetbyt
Copy link
Contributor Author

'Uint32Array',
'Float32Array',
'Float64Array',
'Uint8ClampedArray',
Copy link
Contributor

Choose a reason for hiding this comment

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

Indented too far.

'https://github.com/nodejs/node/blob/master/doc/guides/writing-and-running-benchmarks.md##http-benchmark-requirements ' +
'for further instructions.'));
callback(new Error(`Could not locate required http benchmarker. See ${
requirementsURL} for further instructions.`));
Copy link
Contributor

@mscdex mscdex Apr 17, 2017

Choose a reason for hiding this comment

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

Why the odd indentation here and elsewhere? Shouldn't they line up with the beginning of the string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've tried to stress the subordinate state of the next part. I'll try to decrease indentation.

`| ${fraction(completedRunsForFile, runsPerFile)} runs ` +
`| ${fraction(completedConfig, scheduledConfig)} configs]` +
`: ${caption} `;
return `[${getTime(diff)}|% ${
Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW I'm not particularly a fan of this style to avoid the extra spacing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you elaborate?

Copy link
Contributor

Choose a reason for hiding this comment

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

Using ${ followed by a newline to avoid including spaces due to indentation.

@vsemozhetbyt
Copy link
Contributor Author

Some indentation was reduced. New CI: https://ci.nodejs.org/job/node-test-pull-request/7435/

@@ -54,9 +54,3 @@ function main(conf) {
}
bench.end(n);
}

function buildString(str, times) {
Copy link
Member

Choose a reason for hiding this comment

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

It's funny we even had a recursive implementation :D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I am even sorry to retire it)

' buff.' + fn + '(i, 0, ' + JSON.stringify(noAssert) + ');',
'}'
].join('\n'));
var testFunction = new Function('buff', `
Copy link
Member

Choose a reason for hiding this comment

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

much nicer

@@ -44,7 +44,7 @@ Benchmark.prototype._parseArgs = function(argv, configs) {
for (const arg of argv) {
const match = arg.match(/^(.+?)=([\s\S]*)$/);
if (!match || !match[1]) {
console.error('bad argument: ' + arg);
console.error(`bad argument: ${arg}`);
Copy link
Member

Choose a reason for hiding this comment

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

console.error('bad argument:', arg) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this would imply we need inspect() for arg (as we do for Error objects), while arg is a simple string.

@@ -30,14 +30,14 @@ function main(conf) {
var len = +conf.millions * 1e6;
var clazz = conf.buf === 'fast' ? Buffer : require('buffer').SlowBuffer;
var buff = new clazz(8);
var fn = 'read' + conf.type;
var fn = `read${conf.type}`;
Copy link
Member

Choose a reason for hiding this comment

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

just a reminder that this will make it impossible to run these benchmarks to compare against anything older than 4.0.0. That's not an objection, by any means.

Copy link
Contributor Author

@vsemozhetbyt vsemozhetbyt Apr 17, 2017

Choose a reason for hiding this comment

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

Yes, but there were many other template literals in benchmarks before this edition, including common.js.

Copy link
Contributor

@mscdex mscdex Apr 17, 2017

Choose a reason for hiding this comment

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

At this point I don't think it really matters. I can't see anyone wanting to run the benchmarks for v0.10 or v0.12 anymore, especially since it wouldn't really do any good (it's not as if the V8 team can/will revert to v0.10/v0.12-era V8 code if there is a performance issue in node v4.x+).

Copy link
Member

Choose a reason for hiding this comment

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

Yep, as I said, it's not an objection. Just noting the change. We may want to let folks know just so that they're aware.

Copy link
Member

Choose a reason for hiding this comment

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

I think a lot of benchmarks don't run on v4.x or older anyways...I've seen people on IRC
asking why some benchmark doesn't run and turns out they are using v4.x to run it

Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

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

LGTM

' for (var i = 0; i <= n; i++)' +
' buf.' + method + '();' +
'}';
const fnString = `
Copy link
Member

Choose a reason for hiding this comment

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

Definitely not a fan of multiline literals.

@vsemozhetbyt
Copy link
Contributor Author

@vsemozhetbyt
Copy link
Contributor Author

vsemozhetbyt commented Apr 19, 2017

1 Linux fail seems unrelated. Timeout in sequential/test-benchmark-child-process on Windows. Trying again: https://ci.nodejs.org/job/node-test-pull-request/7522/

UPD. Waiting for #12518

@vsemozhetbyt
Copy link
Contributor Author

vsemozhetbyt commented Apr 19, 2017

vsemozhetbyt added a commit that referenced this pull request Apr 20, 2017
PR-URL: #12455
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
vsemozhetbyt added a commit that referenced this pull request Apr 20, 2017
PR-URL: #12455
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
vsemozhetbyt added a commit that referenced this pull request Apr 20, 2017
PR-URL: #12455
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
vsemozhetbyt added a commit that referenced this pull request Apr 20, 2017
PR-URL: #12455
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@vsemozhetbyt
Copy link
Contributor Author

Landed in bbbb1f6...d8965d5

@vsemozhetbyt vsemozhetbyt deleted the bench-templates branch April 20, 2017 01:51
evanlucas pushed a commit that referenced this pull request Apr 25, 2017
PR-URL: #12455
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@evanlucas evanlucas mentioned this pull request May 1, 2017
evanlucas pushed a commit that referenced this pull request May 1, 2017
PR-URL: #12455
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
evanlucas pushed a commit that referenced this pull request May 2, 2017
PR-URL: #12455
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
vsemozhetbyt added a commit that referenced this pull request May 5, 2017
PR-URL: #12735
Refs: #12455
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
anchnk pushed a commit to anchnk/node that referenced this pull request May 6, 2017
PR-URL: nodejs#12735
Refs: nodejs#12455
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
@gibfahn gibfahn added baking-for-lts PRs that need to wait before landing in a LTS release. lts-watch-v6.x labels May 16, 2017
@jasnell jasnell mentioned this pull request May 11, 2017
MylesBorins pushed a commit that referenced this pull request Jul 10, 2017
PR-URL: #12735
Refs: #12455
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
MylesBorins pushed a commit that referenced this pull request Jul 10, 2017
Backport-PR-URL: #13835
PR-URL: #12735
Refs: #12455
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
MylesBorins pushed a commit that referenced this pull request Jul 11, 2017
Backport-PR-URL: #13835
PR-URL: #12735
Refs: #12455
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
@MylesBorins MylesBorins removed the baking-for-lts PRs that need to wait before landing in a LTS release. label Aug 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
benchmark Issues and PRs related to the benchmark subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants