-
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
Speed up digests with refinements #417
Conversation
Huge thank you to @bouk who did all the work here. Running asset generation in a loop on codetriage.com ``` task "assets:bench" do measure = [] 50.times do |i| puts "== Running (#{i})" measure << Benchmark.measure do `rm -rf tmp/cache/assets/sprockets/v4.0/ ; rm -rf public/assets; touch tmp; time RAILS_ENV=production bundle exec rake assets:precompile` end.real end puts "================ DONE ================" puts measure.join("\n") end ``` I get these numbers: ``` Avg STDev Refinements 10.06318182 0.2621523045 Hash 12.08513782 1.944694921 ``` 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.
Ruby 2.0 uses a different key word http://ruby-doc.org/core-2.0.0/doc/syntax/refinements_rdoc.html |
The minimum ruby version for rails 5 is 2.2.2, we ought to bump the minimum version of Sprockets to the same |
I'm fine with that cc/ @rafaelfranca |
I need to jump in here and state that I think Refinements are the last place we want to look for improvement here. It may be faster than procs, but it will be slower than regular methods and it imposes a severe optimization boundary on implementations with optimizing compilers. |
Ok, so here's two examples that show how easy this is to optimize on JRuby, and I'm not even eliminating the hash. The first uses a plain lambda, and the second uses a hash = {}
hash[:foo] = ->(a, b) { a || b }
loop do
t = Time.now
i = 0
while i < 10_000_000
i+=1
hash.fetch(:foo).call 1, 2
hash.fetch(:foo).call 1, 2
hash.fetch(:foo).call 1, 2
end
puts Time.now - t
end hash = {}
hash[:foo] = method define_method(:anon) {|a, b| a || b}
loop do
t = Time.now
i = 0
while i < 10_000_000
i+=1
hash.fetch(:foo).call 1, 2
hash.fetch(:foo).call 1, 2
hash.fetch(:foo).call 1, 2
end
puts Time.now - t
end And here's numbers on JRuby 9.1.6.0 versus MRI 2.3.1 on the lambda version (the define_method version is about 25% slower on MRI, of course):
It's unfortunate that MRI does not optimize this well. At least we know we're significantly faster than them regardless of how you implement this...except for refinements, which break many of our optimizations. |
Looking at the comment #383 (comment) it looks like the I tried to use
Would love some ideas on syntax for an immutable Proc (i.e. anonymous function without scope lookup) if you've got them https://bugs.ruby-lang.org/issues/12901. That would help a bit here. |
Closing in favor of #439 |
Huge thank you to @bouk who did all the work here. This is a follow up from the conversation at #383 (comment).
Running asset generation in a loop on codetriage.com
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.