JRuby’s Charles Oliver Nutter posted a comment on my previous post asking about the performance penalty I was implying existed when running Ruby on .NET or the JVM:
What is this performance penalty you speak of for JRuby? …. There’s nothing about either Ruby or the JVM that should mean there’s an automatic performance penalty.
Charles, I’m no expert on the JVM, so I have to rely on those who are, like Gilad Bracha:
(Update: in the comments, Steve Swerling points to more detail from Gilad here)
For example, if you don’t have static type information, you cannot use the VM’s highly tuned dynamic dispatch for method calls, and end up doing your own emulation in software. This is tiresome for the implementor, and more importantly, really slow.
Is Gilad’s statement no longer true, or is there some reason it doesn’t apply to JRuby? If so, I’d be extremely interested to hear why.
To get technical: in Java, a typical method call comes down to a single invokevirtual bytecode. But my understanding is that the verifier won’t let you use invokevirtual unless it can prove that the target implements the method you’re trying to invoke. For any given Ruby method call, it’s impossible to prove that, so instead of using invokevirtual directly you have to go through a level or three of indirection. That’s the performance penalty I’m talking about. Now, if the invokedynamic bytecode that Gilad mentions in his post gets implemented, that will certainly help, but AFAICT it’s just a JSR for now.
You’re likely to get similar levels of indirection when doing instance variable access. In Java, an instance variable access becomes a getfield bytecode. But using that relies on knowing all the possible instance variables for a class at compile time. In Ruby, you can’t know that, so at least some of the time you’re going to get a hashtable lookup instead. This is, again, a performance penalty.
Smalltalk VMs don’t have JVM-style verifiers, so that restriction on invokevirtual doesn’t exist (you could say they already have, and always use, a highly optimized invokedynamic). They also have the necessary machinery to add a directly-indexed instance variable halfway through a running program, and do all the re-allocation, re-compilation and pointer twiddling required to make this work, so that you never have to resort to hashtables. This means that Ruby can run on a Smalltalk VM at 100% of the speed that Smalltalk can, whereas I would be very impressed if Ruby ran on the JVM at 5% of the speed that equivalent Java code does. Now, Smalltalk is slower than Java is, but not that much slower: 100% of Smalltalk-on-Strongtalk is a lot better than 5% of Java-on-Hotspot.
Comments