-
Notifications
You must be signed in to change notification settings - Fork 791
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
validate_processor_result! takes up a lot of time #383
Comments
I had some PRs that made this a little better. Here's the first PR #312 I'm not 100% sure, but I think we're accidentally "validating" the processor results twice, since all results will eventually be passed through "digest" and digest converts known classes to a digest, and raises an error on everything else sprockets/lib/sprockets/digest_utils.rb Lines 38 to 80 in 99444c0
If that's the case then we can get rid of this validate processor method safely i think. That's my theory but I haven't spent much time looking into the viability of that solution. Another option could be an API to disable/enable the validation, so maybe you would only turn it on at test time or when there is a problem. |
btw josh doesn't work on this project anymore. |
@schneems If it is the case that the digest alone is sufficient, I was working with @bouk last week to optimize that as well, seeing as how that took a lot of time too. I forked sprockets and have a PR up where I rewrote You can check out the PR if you're interested. |
The digest is doing the exact same work, so it is sufficient. What I haven't done is gone through and proven that all results from the processor are guaranteed to go through the digest function. I'm guessing they will, but I need to prove it before we kill any code. Interesting work with the c-extension. I don't think I would ever make sprockets a gem with a native extension, however i'm not against making it easier to hook into somehow to do that work in C, so we could maybe have a |
@schneems I think a big thing is the way the type switch works, because there's quite a bit of overhead with the Hash-proc thing that Sprockets does right now for every thing in the value that is to be digested. I agree that having a separate C extension is a good idea |
@tenderlove you might be interested on this one. I remember you were talking about this digest code. |
I'll poke at this again, but IIRC we need to stop handling so many types for the digests and switch to polymorphism. Last time I poked at it I had an idea but it wasn't 100% backwards compatible. I'll try to post a patch that isn't backwards compat, but we can at least look at the results. @bouk thanks for reporting this! |
Here is the PR where we switched to this recursive hash #320 which gave me 16% asset generation in code triage. |
Last week I played around with some different approaches for implementing this, including refinements (I've validated that they all return the same digest): https://gist.github.com/bouk/2eef1fffaa88262c364da830dbabc65c. As always, keep in mind that benchmarks are deceiving (I didn't look much into whether the object I'm using is representative of things that are normally digested), but it seems that refinements are viable for this.
Note that my switch implementation is different from the one that was in Sprockets before. |
I'd really like to investigate why we're actually passing so many types to that one function and fix that. ISTM if we went up the callstack and found the source of these complex types and handled them at their source, then we could eliminate this "God function" and speed up the whole system. Does that make sense? |
I think we are building digests out of the hashes that represent an asset, these contain arrays, strings, other hashes etc. I can be available for questions, ping me on campfire. I don't have all the answers but hopefully can help. |
@bouk coming back to your comment on refinements, guess I didn't really read too closely. I re-worked it to be a more-indicative object (one from jquery-rails) and to use benchmark-ips. Thanks for your comment, wish I had investigated more when you posted. Here is the gist: https://gist.github.com/schneems/8c69779766f1ab46a5a262a24de95dba
Turns out refinements are WAYYY faster than the currently used hashes. I'm guessing that is because there is no overhead for context lookup versus with the procs in hashes. Which is what you mentioned. The other surprising thing is that a case statement is faster. That wasn't what I was seeing with another bench in #312. Looking back it seems that while a hash is faster than a case statement in terms of lookup, the actual work done inside of the blocks is dominating the amount of time. Would love other thoughts here with regard to the switch statement. I'm fine using refinements for a perf gain. I'm going to do some more real-world benches today and see what happens. The downside to the refinements is that a subclass of hash or string, etc would pass while currently it is a strict class check. I'm wondering if those things will blow up the cache in another location so maybe this behavior change is fine? class StringFoo < String
end
puts RefinementDigestUtils.digest(StringFoo.new("foo"))
# => 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae |
Here is a PR with some promising results #416 I get these numbers:
Based on this refinements is faster than the current hash (8-16% in a real world assets:precompile app), but there is a high variance. |
JRuby results on your benchmark.
And here's the MRI results:
|
Thanks! Refinements seems to be faster all around. |
@schneems It seems you read the numbers wrong, refinements are actually the slowest of the 3 on JRuby. |
Ah, i did. Ugh. Thanks. |
Since the Here is a fork of the Note: Here are the MRI results
And JRuby results
|
Since we are always looking up these hashes with an identical duplicate (not just the same value, the same object) we can take advantage of Hash.compare_by_identity. Suggested by @dgynn in #383 (comment)
Implemented the |
@bouk are you interested in sending me a patch that just removes this method? We're already essentially doing the same class checks by passing processor results into digest utils. This is duplicated logic. |
…_result [close #383] Remove duplicate check
We're working on porting Shopify to Sprockets 4, and I was looking into a big increase in compilation time. Currently compiling all the assets takes about 3x as long as on sprockets 3
I discovered that
validate_processor_result!
takes up 20% of the total compilation time.Here is a stackprof for a cache-cleared complete compilation of all the assets in Shopify
System configuration
2.2.3
cc @rafaelfranca @josh
The text was updated successfully, but these errors were encountered: