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

Compiling code with a large template literal without placeholders is slower than an equivalent string literal #41448

Closed
merceyz opened this issue Jan 9, 2022 · 6 comments
Labels
linux Issues and PRs related to the Linux platform. performance Issues and PRs related to the performance of Node.js. v8 engine Issues and PRs related to the V8 dependency.

Comments

@merceyz
Copy link
Member

merceyz commented Jan 9, 2022

Version

v17.3.0

Platform

Linux DESKTOP 5.10.60.1-microsoft-standard-WSL2 #1 SMP Wed Aug 25 23:20:18 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

vm

Though most likely deps/V8

What steps will reproduce the bug?

const { compileFunction } = require(`vm`);

const value = `a`.repeat(1024 * 1024 * 5);

console.time(`string`);
for (let i = 0; i < 1000; i++) {
	compileFunction(`"${value}"`, [], {});
}
console.timeEnd(`string`);

console.time(`template`);
for (let i = 0; i < 1000; i++) {
	compileFunction(`\`${value}\``, [], {});
}
console.timeEnd(`template`);

How often does it reproduce? Is there a required condition?

Always

What is the expected behavior?

The amount of time it takes to compile should ideally be the same.

What do you see instead?

Compiling the template literal is significantly slower

string: 19.088s
template: 26.216s

Additional information

No response

@merceyz merceyz changed the title Compiling code with a large template literal without placeholders is slower than a string literal Compiling code with a large template literal without placeholders is slower than an equivalent string literal Jan 9, 2022
@iam-frankqiu iam-frankqiu added vm Issues and PRs related to the vm subsystem. linux Issues and PRs related to the Linux platform. labels Jan 9, 2022
@VoltrexKeyva
Copy link
Member

VoltrexKeyva commented Jan 9, 2022

This is actually intended behavior, template strings don't act the exact same way as normal strings, when a template string is encountered, it is inspected to look for template literals (${}) inside the string, while normal strings are not inspected the same way, therefor making template strings slower to compile than normal strings.

And this inspection is done mainly by the V8 engine, not by the vm subsystem.

@VoltrexKeyva VoltrexKeyva added performance Issues and PRs related to the performance of Node.js. v8 engine Issues and PRs related to the V8 dependency. and removed vm Issues and PRs related to the vm subsystem. labels Jan 9, 2022
@merceyz
Copy link
Member Author

merceyz commented Jan 9, 2022

Indeed, the observation that got me to this issue is that a template literal containing a stringified object literal is (barely) slower than just using the object literal directly. The use case is files like https://github.com/yarnpkg/berry/blob/ce1e0c31781e3fc791cfa86a01348c4f0dba9aec/.pnp.cjs, it contains a large object literal that slows down the parse time. In an attempt to speed it up I stringified the object and parse it with JSON.parse at runtime. I know I can use a string literal for this but I want to preserve the "layout" so it's still readable.

Object literal: https://github.com/yarnpkg/berry/blob/fa346144fbcd783a1c77ab58fbe52cd796fa1f52/.pnp-object.cjs
Template literal: https://github.com/yarnpkg/berry/blob/fa346144fbcd783a1c77ab58fbe52cd796fa1f52/.pnp-template.cjs

hyperfine -w 5 "node -r ./.pnp-object.cjs -e 1" "node -r ./.pnp-template.cjs -e 1"
Benchmark 1: node -r ./.pnp-object.cjs -e 1
  Time (mean ± σ):      87.1 ms ±   0.7 ms    [User: 77.7 ms, System: 10.7 ms]
  Range (min … max):    85.8 ms …  88.5 ms    33 runs

Benchmark 2: node -r ./.pnp-template.cjs -e 1
  Time (mean ± σ):      87.8 ms ±   1.5 ms    [User: 78.0 ms, System: 11.3 ms]
  Range (min … max):    86.5 ms …  92.4 ms    34 runs

Summary
  'node -r ./.pnp-object.cjs -e 1' ran
    1.01 ± 0.02 times faster than 'node -r ./.pnp-template.cjs -e 1'

And this inspection is done mainly by the V8 engine, not by the vm subsystem.

Yeah, I have that as a note in the subsystem section

@merceyz
Copy link
Member Author

merceyz commented Jan 13, 2022

Update: We were able to speed up parsing by using a multiline string literal instead of a template literal yarnpkg/berry#3977

@fhinkel
Copy link
Member

fhinkel commented Jan 14, 2022

Update: We were able to speed up parsing by using a multiline string literal instead of a template literal yarnpkg/berry#3977

Can we go ahead and close this issue?

@merceyz
Copy link
Member Author

merceyz commented Jan 14, 2022

The reported issue (which most likely belongs in the V8 issue tracker and not here) is still valid, V8 is slower at parsing template literals without placeholders than string literals. Somehow V8 is also faster at parsing object literals than template literals which is odd (object vs template in yarnpkg/berry#3977 (comment)).

@targos
Copy link
Member

targos commented Nov 8, 2022

If the issue still exists in v19.x/canary, please open a ticket at https://bugs.chromium.org/p/v8/issues/list
There's not much we can do here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
linux Issues and PRs related to the Linux platform. performance Issues and PRs related to the performance of Node.js. v8 engine Issues and PRs related to the V8 dependency.
Projects
None yet
Development

No branches or pull requests

5 participants