6 ms·
Ruby 2.1 Garbage Collection: ready for production
- matthewmacleod 13y agoI think the takeaway is that there actually is a straight-up bug in the 2.1.1 GC that causes unbounded memory growth, and that the new GC does typically result in higher memory use. The memory issue isn't really that serious, as it seems to be a tradeoff for performance. Although it's not like Ruby is light on memory use as it is… Far more interesting are some of the other issues, like this one: https://bugs.ruby-lang.org/issues/9262 https://bugs.ruby-lang.org/issues/9262 For an app like Discourse 3-10% of request time is occupied looking up methods, due to cache inefficiency. That's amazing, and demonstrates that there's probably still quite a lot of low-hanging performance fruit that Ruby can look to exploit. All of that aside, performance is generally so much better in the 2.1.1 series that it's really worth using.
- rubiquity 13y agoI think it's part bug and part having a GC with only two generations (old and young). When you have to choose between putting these tweener objects somewhere, you have to be more conservative and move them to the old generation. Once a third generation is added (Ruby 2.2?) this will be much smoother. > For an app like Discourse 3-10% of request time is occupied looking up methods, due to cache inefficiency. Hmmm, I thought Ruby 2.1 already had a per-class method cache, or maybe it was just a per-class method cache invalidation, but I don't know how you coud have one without the other. I'll have to reinvestigate this. > That's amazing, and demonstrates that there's probably still quite a lot of low-hanging performance fruit that Ruby can look to exploit. I'm not sure I share as much of a positive outlook. Short of adding JIT compilation, I think the gains from here on out will start to get smaller and smaller. The performance gains of RGenGC were very impressive, though.
- vidarh 13y agoI'm working on a "as static as possible" Ruby compiler as a hobby project, and it's incredibly frustrating at times to see the generated code grow to ridiculous size as I'm getting closer to actually complying with real Ruby semantics... But I do still think there are substantial gains possible. For starters, for most method calls there's no reason to do the expensive method lookups that MRI still uses - cache or no cache - you can use C++ style vtables, as long as you propagate updates to them downwards when a method is re-defined. You do need to be able to fall back to handle dynamically created methods with names not present when you generate the vtables, and optionally reduce waste (as the vtables needs to be the same size for all classes, with unimplemented methods replaced with pointers to method_missing thunks), but in terms of performance you can do fairly well and compared to this GC blowup, the memory waste would be small. But there's also not much alternative but going for proper JIT'ing of at least some things.
- pjmlp 13y agoDynamic languages tend to gain more from JIT as AOT due to such issues. On the other hand, have a look at Dylan, as it might inspire you: http://opendylan.org/ http://opendylan.org/
- vidarh 13y agoFor the method lookup, other than for methods that are dynamically generated with names not known at compile time, the only additional gain you'll get from JIT is by going to full on inline caches, but vtables gets you most of the speedup without the hassle of inline caches and tracing, and doesn't prevent using tracing and inline caching down the line.
- pjmlp 13y agoWith JITs you get devirtualization as well, so no need for vtables. Something possible in AOT as well to certain extent, but it requires a mix of profile guided optimizations coupled with whole programm analysis. Which have issues with dll/so anyway, as those calls cannot be optimized away as in JITs.
- jordanthoms 13y agoKind of depressing how far behind Ruby is from V8, Hotspot, CLR etc in terms of the sophistication of the GC, non-existent JIT etc. Still hoping someday someone will make the investment needed to catch up.
- IPGlider 13y agoLike Rubinius? Or why not JRuby?
- kaffeinecoma 13y agoBecause there's always something different that needs to be done for them once your project starts becoming non-trivial. You might need a different version of a gem (e.g. pure-java Nokogiri), or they're behind recent MRI features. And if you care about concurrency, it's different everywhere. In my personal experience I've found MRI to be the best experience simply because that's what most other people are using, and there's a lot to be gained from being in the mainstream. Don't get me wrong- I would actually love for JRuby to become the de-facto Ruby implementation. So many headaches are caused by native code in gems. And we'd have a solid foundation for GC, concurrency, etc. But that's not the current reality.
- YorickPeterse 13y agoRubinius does not need Gem replacements like you need in JRuby, it still has a compatible CAPI. > [...] or they're behind recent MRI features. Part of this is due to MRI having literally no specification process at all. Python has the PEP system, no such thing exists in Ruby land. People tried to change this in the past (http://rubyspec.org/design/ http://rubyspec.org/design/) but with little to no success so far. As a direct result of this there are only two ways to keep up to date with what changes in Ruby: 1. Follow every issue reported on bugs.ruby-lang.org, forever. 2. Wait until users report issues about something not being present, behaving differently, etc. > And if you care about concurrency, it's different everywhere. This is FUD. An implementation may offer different primitives for concurrency (e.g. Rubinius has Rubinius::Channel) but they also offer shared APIs. For example, the Thread class works across all implementations as you'd expect. Whether you use this on JRuby or Rubinius the end result is the same: proper concurrency.
- adrianlmm 13y agoAnd still, there is no RubyInstaller 2.1 for Windows =(.