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

[BUGFIX release] Avoid re-freezing already frozen objects. #15312

Merged
merged 1 commit into from
Jun 1, 2017

Conversation

rwjblue
Copy link
Member

@rwjblue rwjblue commented May 31, 2017

When a helper is invoked we pass in the arguments, those arguments are frozen (via Object.freeze) so that helpers can't mutate them (and cause issues in the rendering engine itself). When a helper doesn't have hash arguments, we use a shared EMPTY_HASH empty object to avoid allocating a bunch of empty objects for no reason (we do roughly the same thing when no positional params are passed). Since these objects are shared they are being frozen over and over again (throughout the lifetime of the running application). Turns out that there is what we think is almost certainly a bug in Chrome, where re-freezing the same object many times starts taking significantly more time upon each freeze attempt.

This change introduces a guard to ensure we do not re-freeze repeatedly.

Thanks to @krisselden for identifying the root cause.

Fixes #15290

@krisselden
Copy link
Contributor

@@ -38,6 +38,19 @@ export const UPDATE = symbol('UPDATE');

export { NULL_REFERENCE, UNDEFINED_REFERENCE } from '@glimmer/runtime';

let maybeFreeze = () => {};
if (DEBUG) {
maybeFreeze = (obj) => {
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a note here regarding intended usage? I notice you're left the if (DEBUG) { statements even though the function is a noop when !DEBUG and I presume it is for DCE to do its thing. A note here might help a future traveller from thinking they can rely on the noop in non-debug.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good. And yes, that is why I left the if guards.

When a helper is invoked we pass in the arguments, those arguments
are frozen (via Object.freeze) so that helpers can't mutate them
(and cause issues in the rendering engine itself). When a helper
doesn't have hash arguments, we use a shared `EMPTY_HASH` empty
object to avoid allocating a bunch of empty objects for no reason
(we do roughly the same thing when no positional params are
passed). Since these objects are shared they are being frozen
over and over again (throughout the lifetime of the running
application). Turns out that there is what we think is almost
certainly a bug in Chrome, where re-freezing the same object many
times starts taking significantly more time upon each freeze
attempt.

This change introduces a guard to ensure we do not re-freeze repeatedly.

Thanks to @krisselden for identifying the root cause and reporting
the upstream Chrome bug: https://bugs.chromium.org/p/v8/issues/detail?id=6450
@rwjblue rwjblue merged commit e4f4b8f into emberjs:master Jun 1, 2017
@rwjblue rwjblue deleted the avoid-refreezing branch June 1, 2017 00:21
@DLiblik
Copy link

DLiblik commented Jun 1, 2017

@krisselden - thanks for tracing down the root cause - when I posted #15290 I didn't specify which browser we were on - didn't occur to me that the browser itself was the issue (though I guess it was obvious from the profiler...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants