6 ms·
Your description of optimizing Ruby sounds like it is exactly like optimizing JS. JSC’s main optimizing IR (the DFG) is all about understanding the standard lib
by pizlonator 7y ago
Your description of optimizing Ruby sounds like it is exactly like optimizing JS. JSC’s main optimizing IR (the DFG) is all about understanding the standard library semantically. We write the standard library in JS+hacks (called “builtins”) or C++ (pick on a per function basis). Some of those functions have opcodes in DFG, or are built out of primitives that have their own specialized opcodes in DFG.
Maybe the reason why Ruby optimization has problems is nobody has done it the JSC way.
- chrisseaton 7y ago> Your description of optimizing Ruby sounds like it is exactly like optimizing JS. Yes it's the same problem and it's unique to neither Ruby nor JS - but the problem is just scale. Ruby has a larger library, so needs more rewritten from the current C into Ruby+builtins. And then that rewrite doesn't maintain Ruby semantics (the C API currently used does not exactly match Ruby semantics) so a rewrite matching semantics is often very hard. > Maybe the reason why Ruby optimization has problems is nobody has done it the JSC way. People have been trying the approach you use in Core (for over a decade, starting with Rubinius, now TruffleRuby and others), but I think (based on practical experience working on optimising both languages) that it's just a larger problem in Ruby which is why it hasn't been conquered yet.
- pizlonator 7y agoI think that the way you’re describing the solution for Ruby tells me that you don’t see the problem the way that I see it. The problem isn’t rewriting things in builtins. The problem is making an IR in which you can reason about the standard library at scale: reason about its speculation opportunities, all of the implied dependencies and effects, it’s GC impacts, etc. None of the IRs I’ve seen people try for Ruby does that. JSC’s DFG IR does a lot of this. Hence I don’t think folks have really tried the JSC approach for Ruby.
- chrisseaton 7y agoDo you not think the Truffle and Graal IRs do this?
- pizlonator 7y agoNo, they don’t do this. They try to infer specialization from an AST interpreter. That’s not on the same planet as what I’m talking about.
- chrisseaton 7y agoWell I'm not sure what you think you're referring to then. Do you mean an IR where all core library routines are first-class citizens, with their own nodes and information about their semantics encoded so that the compiler can reason about them?
- pizlonator 7y agoYes, that is what we do in JSC. With the caveat that core routines that are very cool don’t get included no matter how simple they are and warm/hot ones get opcodes even if it’s very annoying to do it.
- chrisseaton 7y agoRight, so I get that, and I think so do other people in the small Ruby compiler community. It's just a much larger problem in Ruby due to the size and complexity of the standard library and the presence of more non-local effects (like accessing dynamic caller frames is often required). It's possibly so large that it's not reasonable to do it like that and people are looking for other options, like Truffle's PE. That's why it hasn't been achieved yet - it's not because anyone is unaware of common VM techniques. Another big problem JavaScript doesn't have is that user C extensions are very often on the critical path in Ruby, so you must be able to optimise through them somehow. I don't think JavaScriptCore has any solutions for that we should be trying?
- jashmatthews 7y ago
- pizlonator 7y agoForgot to mention: baseline JIT with PICs. I keep hearing about sophisticated top tier JITs but that won’t do shit if you don’t have a solid baseline JIT.
- jashmatthews 7y ago> The problem is making an IR in which you can reason about the standard library at scale How do you deal with functions written in C++ which invoke functions written in JS?
- pizlonator 7y agoThe important ones are intrinsics. The C++ function is understood by the JIT so well that it just emits the code for it itself. Our C++->JS calling convention sucks, partly because we just avoid going down that path. We do have a C++->JS call IC that we could use more.
- chrisseaton 7y ago> The C++ function is understood by the JIT so well that it just emits the code for it itself. But how would that work for Ruby, where C functions on the critical performance path are often third-party code that the compiler author has never seen before? We'd need to let the JIT understand third-party unseen C functions. There are experiments to do that (Sulong, MIR, Rubinius sort of tried it) but I think it's more of an open problem than you're implying. If you treat calls to unknown third-party C functions as an opaque native call then you're really going to struggle to build a meaningful compilation unit, in my experience.
- tln 7y agoIt looks like the author is intending to use a C to MIR compiler so that existing CRuby code can be inlined into generated MIR code. Third party code could be compiled the same way, right? "The blue parts show the new data-flow for MJIT. When building CRuby, we could generate MIR code for the standard Ruby methods written in C. We can load this MIR code as a MIR binary. This part could be done very quickly."
- chrisseaton 7y agoThat's what MIR is doing, not what JavaScriptCore is doing. Flip thinks this isn't needed for Ruby - '[t]his project is trying to do too many things' - and that JavaScriptCore could do it already. I think that MIR and TruffleRuby think they have to do something else (lifting C code into their IR) shows us that JavaScriptCore's approach isn't quite as immediately applicable as Filip thinks it is.