IronRuby Q&A – What’s Down With Microsoft’s Ruby Implementation In 2010?
IronRuby is an open source Ruby implementation being developed at Microsoft with the .NET CLR in mind. It's reasonably mature and as well being a regular implementation, it provides the ability to use Ruby directly within the Web browser through Microsoft's Silverlight Flash-esque framework. Windows seems to get a bad rap in the Ruby community so we thought we'd turn the spotlight on some of the cool things IronRuby's doing nowadays.
Being based on the .NET CLR presents some unique challenges for IronRuby. So far IronRuby passes 86% of the RubySpec (compared to a 98% pass rate for the MRI on Windows) but this number is creeping up every week. To learn more, I caught up with developer Jimmy Schementi of Microsoft to ask some questions about the project and its workings.
grantmichaels: There are ~150 native extension gems for Ruby, some of which are prolific and often depended upon by other gems. Does your Ruby implementation support FFI (foreign function interface) at present, and/or how much of a priority is running native extension gems going forward?
Jimmy Schementi: IronRuby does not support the Ruby C API which extensions depend on today, and there are no plans to support it either; it’s too difficult to support 100%. However, we do have plans post 1.0 to support FFI, as native code interop from Ruby is very important to IronRuby; it'll make the implementation able to talk directly to native code when running on .NET or Mono. Keep in mind the Win32API is supported in IronRuby, so you can call Win32 functions without writing C#. Today IronRuby developers have to write some C# code which can interface with non-Win32 native code, through the platform invoke APIs, which turns out to be not any more work than using FFI would be, but it does require breaking outside of IronRuby. From there IronRuby lets you consume the C# code without an interop layer.
Passing RubySpec tests is fundamentally important for a modern, robust Ruby implementation. Has this compliance lowered the theoretical performance ceiling of your implementation considerably?
Of course, but can you think of another way? I think every language implementation will tell you that during the very early stages of development they were blazing fast, but as they got closer to implementing compliant Ruby functionality, performance suffered, or it just got harder and harder to keep the implementation at the same level of performance. But that's understandable for a language like Ruby; it puts the burden of cleverness on the language, rather than developer writing applications in Ruby.
Keep in mind though, RubySpec is not for performance testing. And while the ruby-benchmark suite is a fine benchmark suite, the most beneficial performance improvements for IronRuby have come from analyzing and optimizing actual programs, like Rails, RSpec, RubyGems, Cucumber, IRB, etc. RubySpec did play a role here, as it helped make sure we didn't degrade in compliance while using the real applications for performance tuning. Also, we have analyzed MSpec (RubySpec's test runner) on IronRuby for startup and throughput performance.
Closures, continuations, and tail-call optimizations are often discussed in terms of programming language VM's. Which of these attributes are implementable at present, which are expected to be implemented, and which are not possible by design?
Closures aren't really a VM feature, as they are just a block of code executing in a context. As the Dynamic Language Runtime is a language-implementing tool, it does support closures, but IronRuby does not use them; they are forked in IronRuby, though we'd like to put our updates in the next version of the DLR.
However, the Common Language Infrastructure does provide a big part of what a closure is; the block of code, which is called a Delegate. Delegates are essentially function pointers, though they are first-class objects in the CLR, so the can be part of a method signature, stored on other objects, garbage collected, and have any other properties that objects have. Coupled with DynamicMethods, which allow for at-runtime IL (the CLR's intermediate language) generation and conversion to delegates, IronRuby is able to directly invoke CLR code, as well as expose Ruby methods as delegates for CLR code to use.
Although continuations are not part of the ISO CLI, though Mono now supports microthreading and coroutines on top of the CLI. which allows them to implement continuations. Microsoft's CLR does not support continuations, so IronRuby does not have support for the "callcc" today. IronRuby could support true continuations only when running on Mono, but only if IronRuby users pushed for it, and even then we'd really like to avoid different behavior based on the runtime.
IronRuby does not have any support for tail-call optimization. Tail-call optimization is supported by the CLR, but it's not automatic; IronRuby must detect this and correctly emit the opcode for tail-call optimizations, and even then the code needs to fit a specific criteria for the just-in-time compiler to recognize the opcode. However, the CLR's way of supporting it is not why IronRuby doesn't support it; in real Ruby code you'd probably never see tail-call optimization happening, so we haven't seen a need for optimizing this. Tail-call optimization's main benefit in functional languages is to reuse the caller's frame by replacing recursion with a loop, rather than creating a new frame for each recursive call. In Ruby this optimization would be irrelevant if a block or proc was used in the caller, since a new frame would be required for each.
How integral is dynamic feedback-based optimization to reaching a high level of performance? Do you feel it will be possible to maintain acceptable speed into the future without embracing these strategies or are there less well-known alternatives which are potentially more effective?
If by "dynamic feedback-based optimization" you mean "inline caching", then it is very integral to performance of IronRuby. The Dynamic Language Runtime, which IronRuby uses for a target syntax tree, code generation, and interop with the CLR, uses "polymorphic inline caching", which means it caches all call signature a method encounters, turning subsequent calls to the same method (with the same signature) into a cache lookup for a delegate representing the method.
What are your thoughts on the Ruby programming language in the context of distributed programming?
As the number of processors are becoming more and more crucial to the overall speed of CPUs, Ruby needs to make distributed programming a first-class concept in the language to ensure it's continued usage in this changing computing landscape.
The Ruby language is very expressive, and I have no doubt the distributed programming paradigm will fit seamlessly into Ruby's syntax. Ruby's first-class closures and syntactic-sugar around them (blocks) are a natural way of expressing parallel operations. Also Ruby's enumerators and future features like a lazy-array provide the right tools to make parallel programming even easier.
However, Ruby does not have native thread support, so anything that will run truly in parallel will need to run cross-process. While this will work great for MRI, other implementations will have to find ways to make this work well on their platform. I see this as place where the various implementations of Ruby can step up and provide a native runtime-based way of running programs in parallel. For IronRuby, we could utilize the Parallel Extensions libraries to build any Ruby-based parallel support on-top of. In fact, these libraries could be used directly from IronRuby today. For more information about writing parallel software on the CLR, there is a detailed paper as well as a good amount of examples.
Which of 1.8.7 and 1.9.x is your implementation compatible with?
IronRuby 1.0 targets compatibility with the latest patch-level of 1.8.6. IronRuby does have a "-19" flag to enable any Ruby 1.9 features implemented along the way, but it will be disabled for the 1.0 release.
IronRuby 1.x will drop 1.8.6 support and only target the latest version of 1.9. There won't be a specific version which targets Ruby 1.8.7, but those features will probably be the first set of things implemented in the 1.x branch. IronRuby 1.0.x will be a servicing branch for IronRuby 1.0, and that branch will continue to only support 1.8.6.
Do you think further acceptance of Ruby is driven equally by spending man-hours working on performance and by meeting an international body’s specification?
As a Ruby implementer, I welcome a specification for Ruby that everyone agrees on as what makes Ruby the excellent language it is, as long as it doesn't hinder adding future features to the language. Having a specification will probably make it easier for implementations to achieve acceptable performance, because they can design their system with all the requirements of Ruby in sight.
What are your team’s goals for the next quarter, the next year?
IronRuby 1.0 will be released withing the next quarter, with the goals of improving startup significantly, getting RubySpec passing at over 95% across the board, and ensuring compatibility with popular application test suites and scenarios. We will also invest some time in taking advantage of the new features in .NET 4 and Silverlight 4, as well as a push to ensure compatibility with the latest version of Mono.
For the next year, the IronRuby team will focus on 1.9 compatibility, explore tooling options in Visual Studio 2010, and support Ruby and Windows developers to make IronRuby a premier Ruby implementation for Windows.
Besides any obvious platform niche, why might someone benefit from electing to use your Ruby Implementation?
When you ignore the platform niche all implementations of Ruby are pretty similar. In fact, it's the underlying platform that gives one flavor of Ruby advantages over another. For example, MRI has extremely fast startup compared to other flavors, but lacks the rich graphical libraries that Java and .NET have. In the end it’s all about what’s most important to the Ruby user, and in IronRuby’s case there are lots of platform-related benefits, some of which include .NET's integration into the native Windows GUI system, running Rails on Windows through IIS — along-side other ASP.NET website, running in the browser through Silverlight, and the Parallel Extensions library I mentioned earlier.
How much of an advantage is it that you aren't also responsible for maintaining the VM?
Well, for starters it's one less place we have to be responsible for bugs, though working-around any bugs is our responsibility. We don't have to worry about VM issues across different platforms, as the CLR and Mono take care of that for us. Mono is extremely fast in fixing any bugs we discover, like a matter of days, which definitely beats fixing them ourselves. From a ecosystem point-of-view, other things that built on-top of the CLR get interop with Ruby code for free, like running in the browser through Silverlight/Moonlight, and CLR-based debuggers (like MDbg, Visual Studio, and MonoDevelop).
Windows Vista and aboves with .NET pre-installed, most Linux distributions pre-install Mono or it's readily available via packages, and Mono is also easily installable on Macs. Despite that, a number of Rubyists somewhat disregard IronRuby for having any relationship whatsoever to .NET. Are they throwing the baby out with the bathwater?
I'd say so, but they don't necessarily need to "switch" to IronRuby; all Ruby implementations have their own uses. Ruby is unique in that it runs almost anywhere, giving Rubyists a plethora of tools to choose from to solve their problems, while still writing code in Ruby. In fact, this ubiquity makes Rubyists more valuable developers. As Ruby becomes a more accepted language for .NET shops to use, Ruby developers will be in high demand.