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

Fix slow performance of PropTypes.oneOfType() on misses #7510

Merged
merged 1 commit into from
Aug 17, 2016
Merged

Fix slow performance of PropTypes.oneOfType() on misses #7510

merged 1 commit into from
Aug 17, 2016

Conversation

gaearon
Copy link
Collaborator

@gaearon gaearon commented Aug 16, 2016

It used to be slow whenever a type miss occurred because expensive Error objects were being created. For example, with oneOfType([number, data]), passing a date would create an Error object in number typechecker for every item.

The savings depend on how much commonly you used oneOfType(), and how often it had “misses”. If you used it heavily, you might see 1.5x to 2x performance improvements in __DEV__ after this fix.

Thanks to @spicyj @sebmarkbage for ideas on how to fix this.

@gaearon gaearon added this to the 15-next milestone Aug 16, 2016
this.stack = '';
}
// Make `instanceof Error` still work for returned errors.
PropTypeError.prototype = Object.create(Error.prototype);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We got rid of the need to polyfill Object.create in other places I believe. Should we do the same here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't officially support IE8 any more so I'm not that worried. People can polyfill if they want.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just PropTypeError.prototype = Error.prototype should work in our case right?

@sophiebits
Copy link
Collaborator

Can you define toString on the prototype that just returns message? Or maybe you get that for free already. Otherwise good.

@gaearon
Copy link
Collaborator Author

gaearon commented Aug 16, 2016

Can you define toString on the prototype that just returns message? Or maybe you get that for free already.

Yeah, it works thanks to Error.prototype.

It used to be slow whenever a type miss occurred because expensive `Error` objects were being created. For example, with `oneOfType([number, data])`, passing a date would create an `Error` object in `number` typechecker for every item.

The savings depend on how much commonly you used `oneOfType()`, and how often it had “misses”. If you used it heavily, you might see 1.5x to 2x performance improvements in `__DEV__` after this fix.
@ghost ghost added the CLA Signed label Aug 16, 2016
this.stack = '';
}
// Make `instanceof Error` still work for returned errors.
PropTypeError.prototype = Error.prototype;
Copy link

Choose a reason for hiding this comment

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

I think this is tricky. https://github.com/JsCommunity/make-error/blob/master/index.js#L12

Maybe I wrong, not sure.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's easier here since we don't want the stack so we don't need to call the Error constructor.

Copy link
Contributor

@chicoxyzzy chicoxyzzy Aug 17, 2016

Choose a reason for hiding this comment

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

I think code below could be better if we don't care about IE<9 (and I hope nobody does develop in IE<9 😅):

function PropTypeError(message) {
  this.message = message;
}
PropTypeError.prototype = Object.create(Error.prototype, { stack: { value: '' }});

@chenglou
Copy link
Contributor

Crap, blames to me. It's my evil plan to push people to use Flow instead.
Just kidding. Sorry about that...

@bobzhang
Copy link

Interesting, I want to do something similar with BuckleScript. raise real Error object in DEV mode, but faked Error object in Prod, did you have some benchmark

@aweary
Copy link
Contributor

aweary commented Aug 17, 2016

@bobzhang I did a naive benchmark here that shows some noticeable performance gains with this method.

@ghost ghost added the CLA Signed label Aug 17, 2016
@gaearon gaearon merged commit 680685b into facebook:master Aug 17, 2016
@zpao zpao modified the milestones: 15-next, 15.3.1 Aug 19, 2016
zpao pushed a commit that referenced this pull request Aug 19, 2016
It used to be slow whenever a type miss occurred because expensive `Error` objects were being created. For example, with `oneOfType([number, data])`, passing a date would create an `Error` object in `number` typechecker for every item.

The savings depend on how much commonly you used `oneOfType()`, and how often it had “misses”. If you used it heavily, you might see 1.5x to 2x performance improvements in `__DEV__` after this fix.
(cherry picked from commit 680685b)
@MatthewHerbst
Copy link

Should we update the PropTypes docs based on this? They currently strongly suggest returning an Error object when doing custom prop validation.

@gaearon gaearon deleted the faster-oneoftype branch August 20, 2016 09:36
@gaearon
Copy link
Collaborator Author

gaearon commented Aug 20, 2016

I think we should add support for returning strings in a minor release. Then we can document that and remove support for returning errors in the next major version.

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

Successfully merging this pull request may close these issues.

10 participants