-
-
Notifications
You must be signed in to change notification settings - Fork 925
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
Performance issue with ruby meta programming #4879
Comments
For perspective, this is locking up a significant number of jetty web threads because of |
... is this yet another report against (the no longer supported) JRuby 1.7.x or did you guys repro on 9K ? |
got it, that |
We've run this on jruby 9000 as well, same result. |
We can probably improve the synchronization here, but There may be ways to improve this and do a more narrow synchronization, but will be tricky to reduce the cost of |
Could you elaborate this, or point me to something I can read to understand this better? |
could you guys link us to JSON doing |
@ketan you are changing the definition of what foo calls every time you call extend and each time it is a new foo. It does happen to be changing each new instance to the same type every time but this is very hard to see that internally (and we don't and MRI does not either). For that reason we heavily discourage this pattern. DCI also used this pattern and here is an article which delves into it in more detail: |
Thank you for the explanation. Given that this is a known issue (with all ruby implementations?), do you suggest closing this issue out? |
@ketan I am not sure. @headius wonders if we can improve the lock to maybe make extend not stack up as much. If that can be improved we will improve in this situation a tiny bit. We are interested whether a major library is doing this DCI pattern or not. It would be nice to help them speed up if we can. Was this in a library/gem? |
Could you tell us what gem is doing this? Ideally, we'd be able to help them find an alternative. The first time you The cost of Are we significantly slower than MRI here? |
The gem we're using is Our app does not run on MRI, I'm in no position to compare performance with a real app. However a quick benchmark with the example below seems to indicate that JRuby is atleast 2 times faster than MRI is. require 'rubygems'
require 'benchmark/ips'
class ExampleClass
def foo; 42; end
end
module ExampleMixin
def foo; 43; end
end
puts "Running with #{RUBY_DESCRIPTION}"
Benchmark.ips(20) do |bm|
bm.report("without dci") { ExampleClass.new.foo }
bm.report("with dci") do
obj = ExampleClass.new
obj.extend(ExampleMixin)
obj.foo
end
end And the results:
|
@ketan It seems unlikely we will convince these authors to not use the DCI pattern as one or more have done talks on it and I believe think it is worth the cost (e.g. they know the costs); but from your particular perspective a snippet like: SongRepresenter.new(song).to_json should not need to be implemented with DCI at all. I think perhaps the main benefit would be the simple ability to access internal state without access to properties. OTOH, what JSON do you plan on dumping where that state is not already public? Obviously, I am not asking you to write another implementation but the basic premise of decoupling this logic into separate types might not be too hard if you decide that the cost is too high. |
There are ways to optimize this but it's something we're reluctant to do when this pattern is so slow in MRI. Even if we optimize it, most users will not see this pattern run fast unless ruby-core also decides to optimize it. Ideas for optimization:
I'm inclined to close this bug as Won't Fix since we're performing at least as well as MRI on a very poor-performing pattern. If MRI sees fit to optimize this pattern for core Ruby, we'd be happy to work with them. |
@ketan I would present your findings to the authors of that library, and if they really want to use this pattern perhaps encourage them to take it up with MRI. Many folks using the DCI pattern don't realize what a huge impact it has on performance, nor that there are reasonable alternatives. Closing this as Won't Fix for now. |
I'm reopening this because it does appear we can eliminate some (maybe a lot) of the locking here. @kares I believe you are right. The methods that only modify the local I will do a PR (for 9.1.16) that removes most of this synchronization, makes the rest narrower, and ideally improves the overhead of subclass modifications and method invalidation. |
Note: this is going into 9.1.16 because it's high risk to be fiddling about with these locks right before a release. We'll let it bake over the holiday and do a release after the new year. @GaneshSPatil Looks like you may get a partial fix after all! It won't improve the straight-line speed of extend, but it should reduce the contention you reported. Please stand by to test your app against the PR. |
@GaneshSPatil You could start testing that PR any time. Better sooner than later, since I know we'll uncover some concurrency issues in the process of reworking this locking. |
@headius — thanks for the PR. We'll give this a spin today and get back to you. |
@ketan Any update on your findings from the PR? |
@TheKidCoder -- We've tested the PR on the performance test setup, on which we found the performance issue. Our findings:
|
sounds like the original issue is resolved (this can be closed), @GaneshSPatil for the other 'performance' issue I would fill a new bug when you know the details. if you need help figuring it out I am happy to consult. |
@GaneshSPatil we really want to know why there is such a large difference in time. That really sounds like something which should JIT just isn't for some reason (large methods being one common reason -- 9k is a completely different set of internals and some methods could be small enough in 1.7 to JIT but end up being larger in 9k -- this is just an example). Please open a new issue with any information. |
Fixed in 9.1.16 and confirmed by reporter. |
Environment
jruby -v
):jruby 1.7.26 (1.9.3p551) 2016-08-26 69763b8 on Java HotSpot(TM) 64-Bit Server VM 1.8.0_101-b13 +jit [darwin-x86_64]
uname -a
):Darwin **.local 17.2.0 Darwin Kernel Version 17.2.0: Fri Sep 29 18:27:05 PDT 2017; root:xnu-4570.20.62~3/RELEASE_X86_64 x86_64
Description
Extensively Using ruby
extend
functionality blocks threads which in turns causes server performance issues.extend
method calls jrubyaddSubclass
function which is a bottleneck.Background
Many libraries that provide JSON serialization make use of
extend
. Under heavy load, this causes performance degradation.Code Snippet to Reproduce Issue:
Run the above code snippet and capture thread dump. You will see a lot of threads waiting to enter synchronized this block.
here is an example thread dump.
The text was updated successfully, but these errors were encountered: