-
-
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
Improvements to subclass management and method cache invalidation #4886
Conversation
This is a high-risk change but if successful we could remove a great deal of overhead at boot time and for libraries that spin a lot of classes at runtime. First in a series of optimizations for class hierarchy management and method cache invalidation.
@kares @mkristian @lopex @enebo This is a work in progress, but I'll definitely need help reviewing and testing these changes. Anything you can suggest or do to help will be appreciated :-) |
Perhaps I'm wrong — but looking at this PR, a lot of it would likely simplify if |
Ignore me. I think I understand why it can't be. |
I'm not sure what you were thinking, but I guess it would be no better than initializing to null. And the reason we leave such fields blank is so there's no overhead for classes that never have subclasses. It's worth some thought though; elsewhere in the codebase we separate read and write usage so reads can be mostly noops when there's nothing to do. |
LGTM |
Hi, We managed to build our app with this PR, the current bottleneck seems to have been resolved, but we have discovered another similar bottleneck with another synchronized method, which is blocking about 278 threads and grinding the server to a halt as we speak. We have attached the thread dumps at http://fastthread.io/my-thread-report.jsp?p=c2hhcmVkLzIwMTcvMTIvOC8tLWZpbGVfMi0tNS00My0zODs7LS1maWxlXzItLTUtNDUtMTE= if you'd like to take a look.
|
@GaneshSPatil that seems to be the p.s. me walking in your shoes ... would definitely be looking into refactoring/patching that |
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.
LGTM
LGTM but since 9.1.15.0 is out lets put this in today to give it a month to break something. |
@GaneshSPatil This additional case appears to be due to the cost of walking all constants neededing to be invalidated and invalidating them individually. There will be one for every name, but the code (invalidateConstantCacheForModuleInclusion) here does a couple things wrong:
I have a fix for the first two items that should reduce how much locking and JIT impact this has. The third item is a bit trickier. We will need to experiment a bit to figure out which constants really need to be invalidated. |
@GaneshSPatil Please retest. |
Hello @headius, We tested the changes with the same performance setup we have. We don't any blocked threads as of now, but the performance has degraded going from One of our APIs which does JSON serialization used to take 12s to 15s with We'll investigate more into the performance degradation and will provide you an update. |
@headius -- We didn't see any blocked threads around
We have upgraded rails from |
@GaneshSPatil Ok thank you. You may get some good info about the performance issue by using |
whoops we planned on landing this much earlier but we should have some time left before 9.1.16 so it is in! |
In #4879, we got a report of heavy thread contention on the
addSubclasses
method of RubyClass. After investigating the locking involved (at @kares's recommendation), it seems as though much of this locking is excessively restrictive, using a runtime-global lock to prevent multiple threads from modifying any class's list of subclasses at the same time.The subclasses list is also used heavily by method invalidation, and exploring that end of things showed me many places where we could improve performance.
I have removed most of this but we need much more testing, review, analysis to confirm it's safe.
I do not believe this code is used in any hot paths (it's mainly there for ObjectSpace) but I modified the logic to only create a single list for all subclasses and descendants in a given subhierarchy.
It does this by creating a list of invalidators and adding every invalidator from that class on down, recursively. However, this list is usually all GenerationAndSwitchPointInvalidator, and the SwitchPoints are later reprocessed into another array for bulk invalidation. These collection could be cached, thrown out when any descendant is added or removed by calling up-hierarchy. This would reduce the cost of invalidation, but have a higher in-memory cost. It would also provide no benefits (but no detriments) running on a system that creates many classes.
This work has yet to be done, and I have not investigated the actual cost of the invalidator gathering.