-
Notifications
You must be signed in to change notification settings - Fork 51
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
🚀 Rework unesc for a 63+% performance boost to all of postcss. #239
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Impressive investigation + speed-up! I hope you don’t mind I took a look and left some comments with further ideas — WDYT?
Great, let's improve it (from feedback above) and do release |
0e00b18
to
a95c285
Compare
Thanks @mathiasbynens and @alexander-akait for the review! All updated and sped up another 10% or so! |
Btw, I wonder how our DevTools implementation compares: https://source.chromium.org/chromium/chromium/src/+/master:third_party/devtools-frontend/src/front_end/panels/elements/StylesSidebarPane.js;l=3282-3301;drc=fca98ed75dcae230e4a6fb225ae2c46c43d4939b |
@samccone @mathiasbynens Can we do small benchmarks, interesting how it faster/slow |
Hi @alexander-akait yes, at the bottom of the original commit message I added a perf section outlining the old and new implementation. As far as reusing chrome's implementation with a regex this is possible but we would need to update the license of this project to include chrome's licence as well. Finally we can totally do a performance test between chrome's and this implementation. Upon first pass chrome's implementation handles a nice edge case that we do not (invalid unicode), but does not handle a few other decoding cases that the existing test cases require. (Changing this while more correct would be a backwards incompatible change to this core part of postcss which would be a larger undertaking) I will do some further investigation today! 🚀 |
ea4f37a
to
9a358c7
Compare
Hi @alexander-akait and @mathiasbynens a few updates
The numbers are here:
And the testing methodology can be viewed here https://github.com/samccone/postcss-selector-parser/blob/sjs/perf/perf/index.js#L160-L185 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/cc @mathiasbynens Can you look too? It would be nice if a couple more eyes looked at the code, thanks
spent in [`unesc`](https://github.com/postcss/postcss-selector-parser/commits/master/src/util/unesc.js), this was due to the expensive regex checks that were being performed on the fly for every selector in the codebase which looked to be performing quite poorly inside of modern node and v8. ![image](https://user-images.githubusercontent.com/883126/114136698-fdd98a80-98bf-11eb-8068-ace4f6f2274d.png) ---- As an experiment and based on some prior experience with this class of slowdown I migrated the implementation to one that performs a scan through the string instead of running a regex replace. By testing this on my local application I instantly saw the work from this function go from > 900 ms to ~100ms. ![image](https://user-images.githubusercontent.com/883126/114136734-0c27a680-98c0-11eb-82ab-f0c9529fd32d.png) This implementation passes all of the existing test cases and aims to mirror the prior implementation's implementation details :) ----- Based on my application I am seeing the major wins come from purgecss dropping my total application build by multiple seconds! 🔥
surrogates and out of bound codepoint values.
To add some history, slow String.p.replace with global regexp and a callable replacer has been known for a long time, and unfortunately I haven't gotten around to fixing it yet - apologies for that. The previous situation was in fact in a very similar context, see nodejs/node#16986 (comment) and crbug.com/v8/7081. I'm hoping to spend some quality time with regexp later this year. For priorization, it would help if you file a V8 bug report for this, just to point out that this is a real issue, still impacting real cases. In the meantime, it makes sense to use perf workarounds. I know Leszek has been looking at one based on |
Right, the following:
gives me a 50% speedup over this manual string walk:
|
/cc @samccone looks like we have new faster solution 😄 |
@LeszekSwirski unfortunately the solution you listed does not pass all of the test cases added in this change. This is not reflective of your solution being incorrect but rather is more reflective of the existing implementation having multiple bugs that were uncovered and fixed during this code-review process. At this point I see several options 1. ignore the fixes to spec bugs that this change fixes and take the solution you wrote(pros)
(cons)
2. Take the current implementation(pros)
(cons)
3. (maybe 2.a) follow option 2 and follow up with further perf improvements(pros)
(cons)
@alexander-akait my personal recommendation would be to folllow option 3 with continued community investment to continue to iterate on the optimal solution. Please all let me know how you would like to move forward. |
@schuay v8 bug filed! https://bugs.chromium.org/p/v8/issues/detail?id=11664 |
Big thanks |
@samccone Makes sense, although fixing that V8 performance issue of course won't help with the correctness issues you found here. |
In profiling postcss I found that a significant amount of time was being
spent in
unesc
, this was due to the expensive regex checks that werebeing performed on the fly for every selector in the codebase which looked to be performing quite poorly inside of modern node and v8.
Old
As an experiment and based on some prior experience with this class of slowdown I migrated the implementation to one that performs a scan through the string instead of running a regex replace. By testing this on my local application I instantly saw the work from this function go from > 900 ms to ~100ms.
New
This implementation passes all of the existing test cases and aims to mirror the prior implementation's implementation details :)
Based on my application I am seeing the major wins come from purgecss dropping my total application build by multiple seconds! 🔥
Perf testing
I set up a simple perf test here:
samccone@be99c23 which takes all of tailwind's css selectors and then unescapes them 100 times.
(left old, right new)
Previously this simple test took 20.17ms now it takes 7.1ms -- a 63% difference
Finally based on input I did a comparison between the new, old, and chrome's implementation for this work
You can run the test case here https://github.com/samccone/postcss-selector-parser/blob/sjs/perf/perf/index.js, the testing is similar to what I did before but now I run the test cases 1000 times each.
Finally I tested this on tailwind and saw the wins propagating to the ecosystem with this patch!
https://twitter.com/samccone/status/1381423236253032454