-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
support ObjectRestSpread transformation on modern targets for V8 perf #951
Comments
Thanks for the report. It's good to be aware of this issue. Some thoughts:
|
Thanks for the quick & thoughtful reply! It looks like the V8 team has assigned the issue. Waiting a bit to see what they do before committing to a plan makes sense. Just speaking for my team, we'd be happy with your suggestion, i.e.:
p.s. FYI the 2x slowdown is considerably less serious, for us, than the heap behavior. V8 generates a stupendous number of hidden classes for high cardinality object spreads with variable keysets, which leads to the gc behavior documented in the issue my colleague filed against V8. |
Ah is that what's happening. No wonder it's so slow. I think it's ok to ship this without waiting for V8 then. Here's a demonstration of the improvement after this change:
Click to expand
|
FYI: I am planning to change the generated code for this case due to #1017. It turns out However, I have tested the performance for this and it looks like a manual loop that copies the properties over one-by-one is somehow even faster than calling the built-in |
Thanks for the update! FWIW the Babel transpilation pipeline we were using previously generated this polyfill:
and this was the baseline against which we originally observed the perf regression in native object spread. So we would also expect a loop with |
Updates the size goldens for the integration CLI tests to reflect the new payload with APF v13 and the CLI v13-next.7 Overall, there seems to be some increase in the polyfills and a ~400b increase for the `main` bundles. This seems to because with APF v13, the core package no longer uses the downleveled `Object.assign`, but the spread operator directly. ESbuild will then downlevel the spread to `__spreadProps` which seems to come with more transitively-required helpers that end up contributing to the ~400b increase. The spread is downleveled even for the modern browsers this integration test targets, because it is a trick to wrokaround a performance bug in V8. So the size increase is reasonable given the runtime improvement. More details here: evanw/esbuild#951 (comment).
Updates the size goldens for the integration CLI tests to reflect the new payload with APF v13 and the CLI v13-next.7 Overall, there seems to be some increase in the polyfills and a ~400b increase for the `main` bundles. This seems to because with APF v13, the core package no longer uses the downleveled `Object.assign`, but the spread operator directly. ESbuild will then downlevel the spread to `__spreadProps` which seems to come with more transitively-required helpers that end up contributing to the ~400b increase. The spread is downleveled even for the modern browsers this integration test targets, because it is a trick to wrokaround a performance bug in V8. So the size increase is reasonable given the runtime improvement. More details here: evanw/esbuild#951 (comment).
Updates the size goldens for the integration CLI tests to reflect the new payload with APF v13 and the CLI v13-next.7 Overall, there seems to be some increase in the polyfills and a ~400b increase for the `main` bundles. This seems to because with APF v13, the core package no longer uses the downleveled `Object.assign`, but the spread operator directly. ESbuild will then downlevel the spread to `__spreadProps` which seems to come with more transitively-required helpers that end up contributing to the ~400b increase. The spread is downleveled even for the modern browsers this integration test targets, because it is a trick to wrokaround a performance bug in V8. So the size increase is reasonable given the runtime improvement. More details here: evanw/esbuild#951 (comment).
Updates the size goldens for the integration CLI tests to reflect the new payload with APF v13 and the CLI v13-next.7 Overall, there seems to be some increase in the polyfills and a ~400b increase for the `main` bundles. This seems to because with APF v13, the core package no longer uses the downleveled `Object.assign`, but the spread operator directly. ESbuild will then downlevel the spread to `__spreadProps` which seems to come with more transitively-required helpers that end up contributing to the ~400b increase. The spread is downleveled even for the modern browsers this integration test targets, because it is a trick to wrokaround a performance bug in V8. So the size increase is reasonable given the runtime improvement. More details here: evanw/esbuild#951 (comment).
Updates the size goldens for the integration CLI tests to reflect the new payload with APF v13 and the CLI v13-next.7 Overall, there seems to be some increase in the polyfills and a ~400b increase for the `main` bundles. This seems to because with APF v13, the core package no longer uses the downleveled `Object.assign`, but the spread operator directly. ESbuild will then downlevel the spread to `__spreadProps` which seems to come with more transitively-required helpers that end up contributing to the ~400b increase. The spread is downleveled even for the modern browsers this integration test targets, because it is a trick to wrokaround a performance bug in V8. So the size increase is reasonable given the runtime improvement. More details here: evanw/esbuild#951 (comment). PR Close #43431
Heads up that this behavior has changed recently in 0.14.46. TL;DR you now need to do |
Hi. At Airtable, we've been experimenting with esbuild in our toolchain on a large modern Typescript project, and in general we think it's amazing. However, we discovered an unexpected runtime performance regression in the generated JS.
Formerly, our (Babel-based) transpilation process would transform object spreads into invocations of a polyfill. esbuild, with es2018 and later targets, will pass through object spreads as-is. This triggers a surprisingly large perf hit in V8 when using spreads on large objects:
https://bugs.chromium.org/p/v8/issues/detail?id=11536
Empirically, we have unfortunately discovered that this regression (particularly the memory behavior) is large enough that we can't deploy esbuild in production as-is.
Unfortunately, we use BigInt and other modern features in our codebase, and therefore we cannot simply change the esbuild target to es2017 (or, in fact, any other target: the jsTable entries in js_table.go for ObjectRestSpread are all strictly earlier than the entries for BigInt).
We would love to find a solution to situations like this, aside from postprocessing esbuild output with another transpiler, especially because esbuild already has the infrastructure internally to transform object spreads.
We understand that supporting arbitrary subsets of language features as a compilation target is a non-goal. However, what do you think about adding a way of specifying output constraints that target workarounds for known major perf issues in JS engine versions? As a straw man, this could look something like this:
V8
(orV8Perf
, or...), which is defined not to support ObjectRestSpread (currently; if V8 closes the issue linked above, we can note that support, with the appropriate version number).I think this would get us everything we want, without doing violence to esbuild's architecture or design goals. We can put together a PR along these lines if you'd like, but I thought I'd discuss it first since it could be controversial.
Also open to other approaches of course.
The text was updated successfully, but these errors were encountered: