Thursday, January 18, 2007

JRuby Compiler: In Trunk and Ready to Play

Times they are a-changing.

I posted previously on JRuby's compiler work. There have been various iterations of the compiler, many purely prototype and never intended to be completed, and a few genuine attempts at evolving toward full Ruby support. However I believe in the recent weeks I've settled on a design that will carry us to the JRuby compiler endgame.

For the past year, we've emphasized correctness over performance nine times out of ten. When we did focus on performance, it was solely on improving JRuby's interpreter speed, in an attempt to match Ruby's performance in this area and because we knew that JRuby could never entirely escape interpretation. Ruby's just too dynamic for that. So while compatibility with Ruby 1.8.x continued to improve by leaps and bounds, our performance was rather poor in comparison.

This past fall, things started to change. Compatibility reached a point where we could finally be confident about our set of regression tests and our understanding of "how Ruby works" across all its weirdest features. As we understood better the design of the C implementation and the quirky intricacies of the language, we started to see a path to enlightenment. We started to realize how we could support Ruby as it exists today while simultaneously evolving JRuby into a more efficient and cleaner design. And so the performance numbers started to change.

From 0.9.0 to 0.9.1, we had a clean doubling of performance across the board. Our favorite benchmark--RDoc generation--was easily twice as fast, and other simpler benchmarks like fib had similar improvements. 0.9.2 was more of a rushed release for JavaPolis, but we had a good 1/4 to 1/3 speedup even then, since the ongoing refactoring removed another large chunk of overhead from JRuby's core runtime.

From 0.9.2 to current trunk, however, has been a different matter entirely.

The first major change is that we've started to seriously alter the way JRuby does dynamic method dispatching. I did some research, read a few papers, and mocked up and benchmarked a few options. What we've settled on for the moment is a combination of STI for the core classes (STI provides a large table mapping methods and classes to actual code) and various forms of inline caching for non-core classes (basically, for pure Ruby classes; though this is yet to be implemented in trunk). STI provides an extremely fast path for dispatch on those hardest-hit methods, since it reduces calling most core methods to two array indexes and a switch, a vast improvement over the hash lookup and multiple layers of abstraction and framing we had before.

We are continuing to expand our use of STI as it is applicable, and I will soon start exploring options for interpreted-mode inline caching (polymorphic, likely, though I need to run a few trials to get numbers balanced right). So fast dynamic dispatching is well on its way, and will improve performance across the board.

Then there's the compiler work. You have no idea how much it's irritated me to hear people talk about JRuby the past year and say "yeah, but it doesn't compile to Java bytecode." This obviously amounts to pure FUD, but beyond that it totally ignores the complexity of the problem: not a single person on this earth has managed to compile Ruby to a general-purpose VM yet. So complaining about our missing compiler is a bit like complaining that we haven't moved mountains. Honestly people, what do you expect?

Of course, there's the flip side of this statement: compiling Ruby is a hard problem, and I like hard problems. For me it's doubly hard, since I've never written a compiler before. But hell, before JRuby I'd never even worked on an interpreter or language implementation before, and that seems to have gone alright. So there it is...Mount Ruby, waiting to be climbed. And climb it I must!

The current compiler design lives in two halves: the AST-walking half; and the code-generation half. I chose to split these two because it make several things easier. For starters, it allows me to abstract all the bytecode generation logic behind a simple interface, an interface that presents coarse-grained operations like invokeDynamic() and retrieveLocalVariable(). The ultimate implementation of those operations can then be modified at will. It also allows us to evolve the AST independently of the compiler backend, even to the point of swapping in a completely different parser and in-memory code representation (like YARV bytecodes) without harming the evolving code generator backend. So this split helps future-proof the compiler work.

The current design also has another advantage: not all of Ruby has to compile for it to be useful. Currently, as the AST walker encounters nodes, if it finds a node it can't deal with it simply raises an exception. Compilation terminates, and the compiler's client can deal with the result as it will. This leads to a really powerful feature of this design: we can install the compiler now as a JIT and as it evolves more and more code will automatically get optimized. So once we're confident that a given node type is 100% compiling correctly, that node will now be eligible for JIT compilation. As an example, here's the output from a gem installation with the current compiler enabled as a JIT (with my logging in place, naturally):

compiled: TarHeader.empty?
compiled: Entry.initialize
compiled: Entry.full_name
compiled: Entry.bytes_read
compiled: Entry.close
compiled: Entry.invalidate
Successfully installed rake, version 0.7.1
Installing ri documentation for rake-0.7.1...
compiled: LeveledNotifier.notify?
compiled: LeveledNotifier.<=>
compiled: RubyLex.getc
compiled: null.debug?
compiled: BufferedReader.ungetc
compiled: Token.set_text
compiled: RubyLex.line_no
compiled: RubyLex.char_no
compiled: BufferedReader.column
compiled: RubyToken.set_token_position
compiled: Token.initialize
compiled: RubyLex.get_read
compiled: RubyLex.getc_of_rests
compiled: BufferedReader.getc_already_read
compiled: BufferedReader.peek
compiled: RubyParser.peek_tk
compiled: TokenStream.add_token
compiled: TokenStream.pop_token
compiled: CodeObject.initialize
compiled: RubyParser.remove_token_listener
compiled: Context.ongoing_visibility=
compiled: PreProcess.initialize
compiled: AttrSpan.[]
compiled: null.wrap
compiled: JavaProxy.to_java_object
compiled: Lines.next
compiled: Line.isBlank?
compiled: Fragment.add_text
compiled: Fragment.initialize
compiled: ToFlow.convert_string
compiled: LineCollection.add
compiled: Entry_.path
compiled: Entry_.directory?
compiled: Entry_.dereference?
compiled: AttrSpan.initialize
compiled: Entry_.prefix
compiled: Entry_.rel
compiled: Entry_.remove
compiled: Lines.rewind
compiled: AnyMethod.<=>
compiled: Description.serialize
compiled: AttributeManager.change_attribute
compiled: AttributeManager.attribute
compiled: ToFlow.annotate
compiled: NamedThing.initialize
compiled: ClassModule.full_name
compiled: Lines.initialize
compiled: Lines.empty?
compiled: LineCollection.normalize
compiled: ToFlow.end_accepting
compiled: Verbatim.add_text
compiled: FalseClass.to_s
compiled: TopLevel.full_name
compiled: Attr.<=>
Installing RDoc documentation for rake-0.7.1...
compiled: Context.add_attribute
compiled: Context.add_require
compiled: Context.add_class
compiled: AbstructNotifier.notify?
compiled: Context.add_module
compiled: LineReader.read
compiled: null.instance
compiled: HtmlMethod.path
compiled: HtmlMethod.aref
compiled: ContextUser.initialize
compiled: HtmlClass.name
compiled: TokenStream.token_stream
compiled: LineReader.initialize
compiled: TemplatePage.write_html_on
compiled: Context.push
compiled: Context.pop
compiled: HtmlMethod.name
compiled: Context.find_local_symbol
compiled: SimpleMarkup.add_special
compiled: TopLevel.find_module_named
compiled: Context.find_enclosing_module_named
compiled: HtmlMethod.<=>
compiled: ToHtml.annotate
compiled: HtmlMethod.visibility
compiled: HtmlMethod.section
compiled: HtmlMethod.document_self
compiled: LineReader.dup
compiled: Lines.unget
compiled: ToHtml.accept_paragraph
compiled: ContextUser.document_self
compiled: ToHtml.accept_heading
compiled: Heading.head_level
compiled: ToHtml.accept_list_start
compiled: ToHtml.accept_list_end
compiled: ToHtml.accept_verbatim
compiled: SimpleMarkup.initialize
compiled: AttributeManager.initialize
compiled: ToHtml.initialize
compiled: ToHtml.end_accepting
compiled: HtmlMethod.singleton
compiled: Context.modules
compiled: Context.classes
compiled: ContextUser.build_include_list
compiled: HtmlMethod.description
compiled: HtmlMethod.parent_name
compiled: HtmlMethod.aliases
compiled: HtmlClass.parent_name
compiled: ContextUser.as_href
compiled: ContextUser.url
compiled: ContextUser.aref_to
compiled: HtmlFile.<=>
compiled: HtmlClass.<=>
You can see from the output that not only are RubyGems methods getting compiled, but so are stdlib methods and our own Java integration methods. And this is with the current compiler, which doesn't support compiling class defs, blocks, case statements, ... Hopefully you get the picture; this bit-by-bit implementation of the compiler allows us to slowly grow our ability to optimize Ruby into Java bytecodes.

So then, how well does it perform? It performs just dandy, when we're able to compile. Witness the following results for a simple recursive fib algorithm running under Ruby 1.8.5 and JRuby trunk with the JIT enabled.

$ ruby test/bench/bench_fib_recursive.rb
12.760000 1.400000 14.160000 ( 14.718925)
12.660000 1.490000 14.150000 ( 14.648681)
$ JAVA_OPTS=-Djruby.jit.enabled=true jruby test/bench/bench_fib_recursive.rb
compiled: Object.fib_ruby
8.780000 0.000000 8.780000 ( 8.780000)
7.761000 0.000000 7.761000 ( 7.761000)
Yes, that's nearly double the performance of the C implementation of Ruby. And this is absolutely real.

Now JITing is great, and it's obviously carried Java a long ways. The HotSpot JIT is an unbelievable piece of work, and any app that runs a long time is guaranteed to perform better and better as deeper optimizations start to take hold. But We're talking about Ruby here, which starts up at C-program speeds, and runs as fast as it does immediately. So then JRuby needs a way to compete for immediate execution performance, and the most straightforward way to do that is with an ahead-of-time compiler. That compiler is now also available in JRuby trunk.

The name of the command is "jrubyc", and it does just what you'd expect, it outputs a Java class file for your Ruby code. However the mapping from Ruby code to a class file is not as straightforward as you'd expect: a Ruby script may contain many classes or no classes at all, and those classes may be opened and re-opened by the same script or other scripts at runtime. So there's no way to map directly from a Ruby class to a Java class given the strict limitations of Java's class model. But there is a much smaller unit of code that does not change over time, aside from being mercilessly juggled around: methods.

Ruby, in the end, is a creative and sometimes complicated jumble of method "objects", floating from class to class, from module to module, from namespace to namespace. Methods can be renamed, redefined, added and removed, but never can they be directly modified. And so here is where we have our immutable item to compile.

JRuby's compiler takes a given Ruby script and generates the following Java methods out of it: One Java method for the top-level, straight-through execution of the script, including class bodies and "def"s and the like (called "__file__" in the eventual Java class...thanks Ola for the idea), and a Java method for every Ruby method body and closure contained therein, named in such a way as to avoid conflicts. So for the following piece of code:
require 'foo'

def bar
baz { puts "hello" }
end

def baz
yield
end
There would be four Java methods generated: one for the toplevel execution of the script, two for the bar and baz methods, and one for the closure contained within bar. The resulting class file would store these as static methods, so they are accessible from any class or object as necessary, and the toplevel run-through would bind the two Ruby methods to their appropriate names in Ruby-space.

Quite simple, really!

So then an example of the precious, precious JRuby compiler:
$ cat fib_recursive.rb
def fib_ruby(n)
if n < 2
n
else
fib_ruby(n - 2) + fib_ruby(n - 1)
end
end

puts fib_ruby(34)
$ jrubyc fib_recursive.rb
$ ls fib_recursive.*
fib_recursive.class fib_recursive.rb
$ time java -cp lib/jruby.jar:lib/asm-2.2.2.jar:. fib_recursive
5702887

real 0m8.126s
user 0m7.632s
sys 0m0.208s
$ time ruby fib_recursive.rb
5702887

real 0m14.649s
user 0m12.945s
sys 0m1.480s
Again, about twice as fast as Ruby 1.8.5 for this particular benchmark.

Now I don't want you going off and saying JRuby has a perfect compiler that will double the performance of your Rails apps. That's not true yet. The current compiler covers only about 30% of the possible code constructs in Ruby, and the remaining 60% (Update: 70%...that's what I get for late-night blogging) contains some of the biggest challenges like closures and class definitions. It's sure to be buggy right now, and the JIT isn't even enabled by default, plus it has my nasty logging message burned into it, to discourage any production use.

But it is very real. JRuby has a partial but growing compiler for Ruby to Java bytecode now.

And oh my, look at the time. Tonight I have to finish my visa application for a trip to India, nail down schedules and descriptions for several upcoming talks, and prepare some slides and notes for presentations in the coming weeks. You will see more about the Java compilation and our developing YARV/Ruby 2.0 bytecode support over the next couple months...and you can expect JavaOne to be an interesting time for Ruby on the JVM this year ;)

Friday, January 12, 2007

Ruby Compiler Fun: AOT and JIT Compilation

Who knew writing a compiler could be so much fun.

I managed to accomplish two things tonight. It's late and I have a flight home tomorrow, so I'll be brief.

jrubyc: JRuby's Ahead-Of-Time (AOT) Compiler

I have whipped together the very barest of command-line, ahead-of-time compilers, along with a simple script to invoke it.

~/NetBeansProjects/jruby $ jrubyc
Usage: jrubyc <filename> [<dest>]
It's mostly just a very thin wrapper around the existing compiler code, so it can only compile constructs it knows about. However, for really simple scripts without any unrecognized nodes, it works fine:
~/NetBeansProjects/jruby $ cat samples/fib.rb
# calculate Fibonacci(20)
# for benchmark
def fib(n)
if n<2
n
else
fib(n-2)+fib(n-1)
end
end
print(fib(20), "\n")
~/NetBeansProjects/jruby $ jrubyc samples/fib.rb tmp
~/NetBeansProjects/jruby $ ls tmp/samples
fib$MultiStub0.class fib.class
At the moment, two classes are generated; one is a class to hold the script entry points and the other is a stub class for all the actual blocks of code contained within the script (toplevel code, method code, etc). This will soon be a single class file, so pay the MultiStub no mind.

We can then execute the script like you'd expect, specifying the JRuby and ASM jar files on the classpath:
~/NetBeansProjects/jruby $ export CLASSPATH=lib/jruby.jar:lib/asm-2.2.2.jar:tmp        
~/NetBeansProjects/jruby $ java samples/fib
6765
Huzzah! Compilation!

Now of course, as I mentioned, this only compiles scripts containing constructs it knows about. If you try to compile a script it can't handle, you'll get an error:
~/NetBeansProjects/jruby $ jrubyc lib/ruby/1.8/singleton.rb
Error -- Not compileable: Can't compile node: ModuleNode[]
The compiler currently supports only literal fixnums, strings, and arrays, simple method definitions, while loops, if/else, and calls that don't involve blocks or splatted arguments. More will come as time progresses. The benefit of building the compiler piecemeal like this becomes more apparent in the next section...

JIT Compilation

The current compiler only understands enough of Ruby to handle my experimentation and research. The compiler also does not output one-to-one Ruby-to-Java classes or even a single large method: it outputs a class containing a method for every semantically separate block of code in a given script. In Ruby's case, that means toplevel code, code found within the body of a class, and code found within the body of a method definition. By combining these two traits, we have everything necessary for a simple JIT.

A JIT, or Just-In-Time compiler, performs its compilation at runtime, usually based on some gathered information about the executing code. HotSpot, for example, has an extensive array of optimizations it can perform on running code just by watching how it executes and eliminating unnecessary overhead. My vastly simpler JIT uses a much more basic metric: the number of times a method has been invoked.

The actual compiler code is the same as that used for the AOT compiler, with one major difference. Instead of the generated code being dumped to a file for later execution, it's immediately loaded, instantiated, and snuggled away in the same location where interpreted code used to live. The logic goes like this:
  1. A method is called. We'll name it "foo"
  2. foo's code is written in Ruby, so it's just a sequence of AST nodes to be interpreted
  3. we interpret foo's nodes, but each time we increment a counter. When the counter reaches some number (currently 50), the compiler kicks in
  4. if the code can't be compiled, we continue to interpretation, but we set a flag and never try to compile again
  5. if the code can be compiled, we save the generated code and use it for all future invocations
Because the compiler can generate these small pieces of code, we're able to JIT Ruby code that was not compiled before execution began, gaining the benefits of a compiled platform without losing the flexibility of an agile script-based development model. It also means we can start benefiting from bytecode compilation even before the compiler is complete.

So how well does it perform? Very well, provided you don't go outside the narrow range of AST nodes the script supports:
~/NetBeansProjects/jruby $ cat test/bench/bench_fib_recursive.rb
require 'benchmark'

def fib_ruby(n)
if n < 2
n
else
fib_ruby(n - 2) + fib_ruby(n - 1)
end
end

puts Benchmark.measure { fib_ruby(30) }
puts Benchmark.measure { fib_ruby(30) }
Here we have a fib benchmark script with a few nodes the compiler can't handle. For example, the blocks at the bottom of the script won't compile correctly at present. So it's a good candidate for the JIT.

Once the JRuby JIT's been wired up, we can simply run the code as normal:
~/NetBeansProjects/jruby $ jruby test/bench/bench_fib_recursive.rb
compiled: Object.fib_ruby
2.877000 0.000000 2.877000 ( 2.876000)
2.955000 0.000000 2.955000 ( 2.955000)
You will notice the "compiled" logging output I currently have in the JIT. The only method hit hard enough to be compiled during this run was the fib_ruby method defined on the toplevel Object instance. Now this performance is drastically increased over the current trunk, largely due to compilation but also due to a faster dynamic method invocation algorithm we're experimenting with. And there's still a lot of optimization left to be done at both the compiler and runtime levels. But it's already a vast improvement over JRuby from even a month ago. Things are moving very quickly now.

We also look better running under the Java 6 server VM. The "server" VM performs more aggressive optimizations of Java code than does the default "client" VM. Generally this is because the optimizations involved cause the server VM to start up a bit more slowly, since it waits longer and gathers more information before JITing. However in this case, the results are very impressive when we compare the JRuby JIT running under the Java 6 server VM against Ruby 1.8.5:
~/NetBeansProjects/jruby $ jruby SERVER test/bench/bench_fib_recursive.rb
compiled: Object.fib_ruby
1.645000 0.000000 1.645000 ( 1.645000)
1.452000 0.000000 1.452000 ( 1.453000)
~/NetBeansProjects/jruby $ ruby test/bench/bench_fib_recursive.rb
1.670000 0.000000 1.670000 ( 1.677901)
1.660000 0.000000 1.660000 ( 1.671957)
The future's looking pretty bright.

None of this code is in trunk at the moment, but it should land fairly soon. The AOT compiler may come before the JIT, since it's minimally invasive and won't affect normal interpreted mode execution. Look for both to be available in JRuby proper within a week or two, and watch for the compiler itself move toward completion over the coming weeks.

Saturday, January 06, 2007

Five Things About Me

Tor, you sneaky devil. You tagged me before anyone else had a chance. You grabbed the brass ring. Kudos.

So to continue the "5 Things" meme (for the record, I really hate the word "meme"), I present for you five things you probably don't know about me. Actually, some of you will know some of these facts, but I doubt any of you will know them all. I've tried to pick the most quirky or interesting bits out of my otherwise humdrum life.

  • Some time in 1998, I became the lead developer on the LiteStep project. LiteStep was a very popular replacement for the Explorer desktop shell on Windows during the late 90s. It provided a new taskbar, desktop window, NeXT-like dock, and pluggable UI and theming system. For hardcore users tired of the boring Explorer UI, it was the state of the art.

    Originally created by a fellow named Francis Gastellu, it had by 1998 grown rather quiet. At the time, the codebase was silently fading away, with none of the original developers still working on the project and few active developers interested in or able to make a large time commitment to get LiteStep going again. I discovered LiteStep and was attracted by its ability to replace the entire desktop Look & Feel of my Windows machines. I had also been an avid Win32 developer, releasing the shareware program "Hack-It" to some minimal financial success. However the LiteStep code was in really rough shape.

    Almost all the logic was packed into a single large C file that controlled the main desktop window. All the other modules were heavily dependent on this one piece of code, which ultimately crippled LiteStep's ability to incorporate certain types of UI plugins into a user's desktop. I tackled the problem in two ways:

    1. I started converting the core plugins to C++ pure virtual classes and implementations, to allow for a more componentized system
    2. And I reworked all the critical functionality from the desktop module into a central runtime, allowing all other modules to finally remove their desktop dependencies

    Over the next year, LiteStep started to grab the attention of the desktop theming community once again. "Skinning" in general really took off during this time, with the launch of new shells GeoShell, DarkStep, and others. An article published in Wired (for which I was interviewed but not quoted) detailed this new movement.

    Sadly, with the release of theming capabilities in Windows XP, the rise of Linux desktops, and the rebirth of Macintosh with OS X, LiteStep has long since fallen from grace. But to this day I still have the odd person walk up to me and thank me for my efforts during that time. LiteStep, we barely knew ye.

    I suppose an addendum to this item is that for many years I wrote at least as much Win32 C++ code as I did Java, and I still have the programming guides to prove it. How's that for diversity?

  • I do not remember a time in my life I was not in front of a computer. The first computing experience I can remember was programming and playing with BASIC on my Atari 400, writing little games and buying programming books containing short apps I could type in...carefully...one finger at a time. I remember saving my programs to the Atari cassette tape drive and praying, praying, praying it would actually take. I remember dialing up to text-based information services at 300bps over an acoustic coupler. In third grade, a mentor came to my elementary to teach me to program in Apple BASIC, though I never owned an Apple computer until my current MacBook Pro.

    Throughout gradeschool and highschool, my primary interests lie with computers. I ran a BBS called "Terminal Nightmare" (clever, eh?) for which I toiled many hours creating ANSI graphics and advertising on more popular boards. I brought C programming manuals to school in 8th grade to read during slow periods. I wrote C and assembler code on embedded processors for my dad's electronics design ventures in 9th grade. And so on and so forth. I've been a computer geek as long as I can remember, and I've never had a problem with that.

    Toward the end of highschool I started thinking about degree programs. I initially started my post-secondary education in Organic Chemistry, and completed the first two years of requirements. But I hated labs. Some time during the second year, I discovered that there was something called a "Computer Science" degree. Oh, hell yes. From then on I never performed another titration or chromatograph, and I couln't be happier.

  • When I am not programming (which is extremely rare) I am an enthusiast of complete-information strategy games. I have spent some amount of time reading about and studying Go, which is my favorite game. I enjoy playing various Shogi variants (including Shogi, Chu Shogi, Tenjiku Shogi, and Tori Shogi), though I don't claim to be good at any of them. I will play Xiang Qi, but it's not one of my favorites, and I have not learned any particularly good strategies. I also play Chess, having been taught by my father at an early age.

    Occasionally me and a few local friends will get together and play these games until the wee hours of the morning. Some people have LAN parties; we have strategy gaming parties. We most frequently play Bughouse when we can find four people and two clocks, but we often just get together to play the above games one-on-one.

    And by "complete-information" games, I mean those in which there is no element of chance. I do not enjoy dice games, and I will play card games only if present company prefers such games. My opinion is that if I lose a game, I would much rather it be due to my own ineptitude than due to random chance.

  • I was one of the best fight-game players in local arcades in the late 1990s. Oddly enough, I was never drawn to Street Fighter, but I spent literally thousands of dollars over the years getting good at the Mortal Kombat and Killer Instinct series of games from Midway. My friend and I would generally spend most weekend nights at arcades, usually playing for minimum cost against players short on skill but long on quarters. We got quite good.

    I was also pretty heavily addicted to those games. During my first two years at the University of Minnesota, I generally skipped class to play. There was such a rush from getting a higher combo, or beating a new player who tried to represent. I also made many friends in those arcades whose names I never knew and whom I have never seen since...but there was a bond among us gamers.

    When I had the means, I began to collect arcade machines. Unfortunately, the means ran dry after only a few purchases, but I've been happy to have them. I own the following arcade machines, stowed in my basement and occasionally played:


    I also own the hollowed-out remnants of an old Gun Fight cabinet. I intended to restore it, but the side art and wood were in very poor shape. It's rotting in the garage.

    I'd love to have a Ms PacMan, Q*Bert, or Tron machine. Unfortunately, so would the rest of the world.

  • I write and eat left-handed, though I prefer my right hand for almost everything else. Unfortunately, like most lefties, this means I can't use writing utensils that may smear or smudge. You lefties know what I'm talking about: the dreaded "pencil hand" you get from dragging your hand through what you've just written. In junior high I finally got tired of having to wash pencil lead off my hand every day, and for several years I utilized a novel solution:

    I wrote backwards.


  • --

    I'll tag Ola Bini, Nick Sieger, Pat Eyler, Evan Phoenix, and Jochen Theodorou to blog "5 Things" people might not know about them.

Friday, January 05, 2007

Ruby Breaks TIOBE Top Ten; Declared Language of the Year

The headline says it all, really!

The TIOBE Programming Community Index measures language popularity based on "the world-wide availability of skilled engineers, courses and third party vendors" using the major search engines. It's not not a terribly scientific way to measure popularity, but I'm not sure anyone has a better index.

Ruby has been moving up every month during 2006 and for the first time has broken the top ten in January 2007. TIOBE also declared it the "Programming Language of 2006", which comes as no surprise to us Rubyists who love the language so much.

Congratulations, Ruby!

Thursday, January 04, 2007

New JRuby Compiler: Progress Updates

I've been cranking away on the new compiler. I'm a bit tired and planning to get some sleep, but I've gotten the following working:

  • all three kinds of calls
  • local variables
  • string, fixnum, array literals
  • 'def' for simple methods and arg lists
  • closures
Now that last item comes with a big caveat: I have no way to pass closures I create. The code is compiling, basically as a Closure class that you initialize with local variables and invoke. But since block management in JRuby is still heavily dependent on ThreadContext nonsense, there's no easy way to pass it to a given method. So the next step to getting closures to work in the compiler is to start passing them on the call path as a parameter, much like we do for ThreadContext.

I've managed to keep the compiler fairly well isolated between node walking and bytecode generation, though the bytecode generator impl I have currently is getting a little large and cumbersome. It's commented to death, but it's pushing 900 LOC. It needs some heavy refactoring. However, it's behind a fairly straightforward interface, so the node-walking code doesn't ever see the ugliness. I believe it will be much easier to maintain, and it's certainly easier to follow.

In general, things are moving along well. I'm skipping edge cases for some nodes at the moment to get bulk code compiling. There's a potential that as this fills out more and handles compiling more code, it could start to be wired in as a JIT. Since it can fail gracefully if it can't compile an AST, we'd just drop back to interpreted mode in those cases.

So that's it.

...

Ok, ok, here's performance numbers. Twist my arm why don't you.

(best times only)

The new method dispatch benchmark tests 100M calls to a simple no-arg method that returns 'self', in this case Fixnum#to_i. The first part of the test is a control run that just does 100M local variable lookups.
method dispatch, control (var access only):
interpreted, client VM: 1.433
interpreted, server VM: 1.429
ruby 1.8.5: 0.552
compiled, client VM: 0.093
compiled, server VM: 0.056
Much better. The compiler handles local var lookups using an array, rather than going through ThreadContext to get a DynamicScope object. Much faster, and HotSpot hits it pretty hard. At worst it takes about 0.223s, so it's faster than Ruby even before HotSpot gets ahold of it. The second part of the test just adds in the method calls.
method dispatch, test (with method calls):
interpreted, client VM: 5.109
interpreted, server VM: 3.876
ruby 1.8.5: 1.294
compiled, client VM: 3.167
compiled, server VM: 1.932
Better than interpreted, but slow method lookup and dispatch is still getting in the way. Once we find a single fast way to dynamic dispatch I think this number will improve a lot.

So then, on to the good old fib tests.
recursive fib:
interpreted, client VM: 6.902
interpreted, server VM: 5.426
ruby 1.8.5: 1.696
compiled, client VM: 3.721
compiled, server VM: 2.463
Looking a lot better, and showing more improvement over interpreted than the previous version of the compiler. It's not as fast as Ruby, but with the client VM it's under 2x and with the server VM it's in the 1.5x range. Our heavyweight Fixnum and method dispatch issues are to blame for the remaining performance trouble.
iterative fib:
interpreted, client VM: 17.865
interpreted, server VM: 13.284
ruby 1.8.5: 17.317
compiled, client VM: 17.549
compiled, server VM: 12.215
Finally the compiler shows some improvement over the interpreted version for this benchmark! Of course this one's been faster than Ruby in server mode for quite a while, and it's more a test of Java's BigInteger support than anything else, but it's a fun one to try.

All the benchmarks are available in test/bench/compiler, and you can just run them directly. If you like, you can open them up and see how to use the compiler yourself; it's pretty easy. I will be continuing to work on this after I get some sleep, but any feedback is welcome.

Wednesday, January 03, 2007

InvokeDynamic: Actually Useful?

Over time I've become less convinced that hotswappable classes would be an absolute requirement for the proposed invokedynamic bytecode to be useful, and more convinced that there's a number of ways a dynamic language like Ruby or Groovy could utilize the new bytecode. This post gives a little background on invokedynamic and attempts to summarize a few ideas off the top of my head.

Many folks, myself included, have long held that the proposed invokedynamic bytecode would only be useful if coupled with hotswappable classes. Hotswapping is the mechanism by which we could alter class structure after definition and have existing instances of the class pick up those changes. It's true this would be required if we were to compile Ruby all the way to bytecode; since Ruby classes are always open, we need the ability to add and remove methods without destroying already-created instances. The argument goes that if invokedynamic requires a dynamically-invoked method to exist on a target receiver's type, then we would only ever be able to invokedynamic against compiled Ruby code if we could continue to alter those types when classes get re-opened.

I do believe that hotswapping would be useful, but it's fraught with many really difficult problems. To begin with, there's Java's security model, whereby a class that's been loaded into the system *can not* be modified in most typical security contexts. The JVM does have the ability to replace existing method definitions at runtime, but that's generally reserved for debugging purposes, and it doesn't allow adding or removing methods. It also does not currently have the ability to wholesale remove and replace a class that has live instances, and it's an open research question to even consider the ramifications of allowing such a thing.

So what are the alternatives? Gilad Bracha proposed having the ability to attach methods dynamically to a given static class at runtime. This would perhaps be similar to the CLR's "dynamic methods". This idea perhaps has more merit...one issue not addressed by hotswappable classes is that even once we compile Ruby to bytecode, it's still dynamic and duck-typed. Would all methods accept Object and return Object? Is that useful? By specifically stating that some methods are dynamic and mutable (in the case of a Ruby class, likely all methods we've compiled), you effectively create the equivalent of hotswapping without breaking existing static types and their security semantics.

But this is all research that could and perhaps should occur outside invokedynamic, and it all may or may not be related. So then, can invokedynamic be useful with these class-structure questions unanswered? What does invokedynamic mean?

To me, invokedynamic means the ability to invoke a method without statically binding to a specific type, and perhaps additionally without specifying static types for the parameter list. For those that don't know, when generating method-call bytecodes for the JVM, you must always provide two things in addition to the method name: the class within which the method you're invoking lives and the precise parameter list of the method you want to call. And there's not much wiggle room there; if you're off on the target type or if the receiver you're calling against has not yet been cast to (or been determined to match) that type, kaboom. If your parameter list doesn't match one on the target type, kaboom. If your parameters haven't been confirmed as being compatible with that signature, kaboom. Perhaps you can see, then, why writing a compiler for the JVM is such a complicated affair.

So there's potential for invokedynamic to make even static compilation easier. Without the need to specify all those types, we can defer that compile-time magic to the VM, if we so choose. We don't have to dig around for the exact signature we want or the exact target type. Given a receiver object, a method name, and a bundle of parameter objects, invokedynamic should "do the right thing."

Now we start to see where this could be useful. Any dynamic language on the JVM is going to be most interesting in the context of the platform's available libraries. Ruby is great on its own, and there's certainly an entire (potentially large) market segment that's interested in JRuby purely as an alternative Ruby runtime. But the larger market, and the more intriguing application of JRuby, is as a language to tie the thousands of available Java libraries together. And that requires calling Java code from Ruby and Ruby code from Java with as little complexity and overhead as possible.

Enter invokedynamic.

Now I've only recently started to see how invokedynamic could really be useful even without dynamic methods or hotswappable classes, so this list is bound to grow. I'd love to have all three features, of course, but here's a few areas that invokedynamic alone would be useful:

  • Our native implementations of Ruby methods can't really be tied to a specific concrete class, since we have to be able to rewire them at runtime if they're redefined. If invokedynamic came along with a mechanism for doing a Java-based "method_missing", whereby we could intercept dynamic calls to a given object and dispatch in our own way, we could make use of the bytecode without having hot-swappable classes.
  • It would also aid compilation and code generation. In my work on the prototype compiler, one of the biggest stumbling blocks is making sure I'm binding method calls to the appropriate target type. I must make sure the receiver of a method has been casted to the type I intend to bind to or Java complains about it. If there were a way to just say invokedynamic, omitting the target type, it would make compilation far simpler; and I don't believe HotSpot would have to do any additional work to make it fast, since it already has optimizations under the covers that are fairly type-agnostic.
  • To a lesser extent, invokedynamic could push the smarts of determining appropriate method signatures onto the VM. I would supply a series of parameters and a method name, and tell the VM to invokedynamic. The VM, in turn, would look at the params and name and select an appropriate method from the receiving object. This is in essence all that's needed for real duck typing to work.
This last item calls out a perhaps surprising area that invokedynamic would be very useful: invoking Java code from a dynamic language.

When calling Java code from Ruby, for example, all we really have to work with are two details: a method name and potentially an arity. We can do some inference based on the actual types of parameters, but there's a lot of magic and a number of heuristics involved. If there were a JVM-native mechanism for calling arbitrary methods on a given object, without having to statically bind to those methods, it would eliminate much of our Java integration layer.

All told, I think invokedynamic would definitely be much more than a PR stunt, as some have claimed. It would eliminate one of the most difficult barriers to generating JVM bytecodes by allowing arbitrary method calls that aren't necessarily bound to specific types. I for one would vote yes, and I plan to throw my weight behind making invokedynamic do everything I need it to do...with or without hotswapping.

Tuesday, January 02, 2007

Groovy 1.0 is Released!

Congratulations to the Groovy team on their release of Groovy 1.0! Groovy is another dynamic language for the JVM inspired by features in Smalltalk, Python, and of course Ruby. It's been a long time coming, and a lot of hard work involved, but Groovy 1.0 is finally here.

See the announcement from Guillaume Laforge, one of the Groovy team members.

Here's hoping there's a bright future of cooperation between the Groovy team and the other dynamic languages for the JVM.

Monday, January 01, 2007

Welcome Nick Sieger to the JRuby Team

The team has grown again! After I asked the JRuby community to nominate a new team member, based on past code, mailing list, documentation, or other contributions, a number of folks thought Nick Sieger would be a good addition. And we agreed.

Nick is the original author of the ActiveRecord-JDBC connector, and has done a lot of work wiring JRuby up with NanoContainer. He's been an active member of the mailing lists and you've probably all read his blog at some point...if only for his excellent summary posts from RubyConf 2006. Even better, Nick hails from the Minneapolis area like Tom and I, and we attend the same Ruby user group meetings with the Ruby Users of Minnesota.

We also expect Nick will bring his familiarity with Maven 2 and his professional experience leading both Java and Ruby-based projects. He's a good developer and a good leader to add to the team.

Hopefully this will also serve as a reminder that JRuby is a true Open Source project, and anyone with Ruby and/or Java experience can easily start helping out. The team and the community continue to grow, as does Ruby's potential on the JVM.

Welcome to the team, Nick!

Wednesday, December 27, 2006

Making Dynamic Invocation Fast: An Idea?

Evan Phoenix (of Rubinius fame) were discussing dynamic dispatch today on #rubinius, sharing our caching strategies and our dispatch woes. We talked at length about various strategies for speeding dispatch, cache invalidation mechanisms, compilation techniques, and so on. All gloriously fun stuff.

So at one point I related to him the extended version of my plans for speeding up dynamic dispatch. I relate it now to you to hopefully spur some discussion. There are no original ideas anymore, right? Or are there?

The initial experiment with Fixnum basically was the static version of my fuzzy vision. During parse time, a very trivial static table mapped method names to numbers. I only handled three method names, +, -, and <. Then those numbers were passed to Fixnum during method dispatch, where a very trivial static switch statement used them to "fast dispatch" to the appropriate methods.

The ultimate vision, however, is much grander.

The general idea is that during dynamic dispatch, we want to use the method name and the class as keys to locate a specific method instance. The typical way this is done is by keeping a hashtable on every class, and looking up methods by name. This works reasonably well, but each class must have its own table and we must pay some hashtable cost for every search. This is compounded when we consider class hierarchies, where the method we're looking for might be in a super class or a super class's super class, ad infinatum.

Retrieving the class is the easy part; every method we want to dispatch will have a receiver, and receivers have a reference to their class.

So then, how do we perform this magic mapping from M method names and N classes to a given method? Well hell, that's a matrix!

So then the great idea: build a giant matrix mapping method names (method IDs) and classes (class IDs) to the appropriate switchable value. Then when we dispatch to the class, we use that switchable value in a neatly dense switch statement.

Let me repeat that in another way to make it clear...the values in the matrix are simple "int" values, ideally as closely-numbered (dense) as possible for a given class's set of methods. In order to dispatch to a named method on a given object, we have the following steps:

  1. Get the method ID from the method (likely calculated at parse or compile time)
  2. Get the class ID from the class (calculated sequentially at runtime)
  3. Index into the matrix using method ID and class ID to get the class-specific method switch value
  4. Use the switch value to dispatch (via a switch statement, of course) to the appropriate method in the target class
Ignoring the size of this matrix for a moment, there are a number of benefits to this data structure:
  • We have no need for per-class or inline method caches
  • Repopulating the switch table for a given class's methods is just a matter of refilling its column in the matrix with appropriate values
  • Since the matrix represents all classes, we can avoid searching hierarchies once parents' methods are known; we just represent those methods in the matrix under the child's column and fast-dispatch to them as normal
  • We can save off and reconstitute the mapping to avoid having to regenerate it
...and there's also the obvious benefit: using the switch values stored in the matrix, we can do a fast dispatch on the target object with only the minimal cost of looking up a value in the matrix. An additional benefit for C code would be simply storing the address of the appropriate code in the matrix, avoiding any switch values completely.

Now naturally we're talking about a big matrix here, I'll admit that. A fellow by the name of "Defiler" piped up and said an incomplete app of his showed 1147 unique method names across 1258 classes. That works out to a matrix with 1.4M elements. In the Java world, where everything is 32 bits, that's around 6MB of memory consumed just for method mapping. Acceptable? Perhaps. But what about using a sparse matrix?

A sparse matrix attempts to be as efficient as possible when there are large numbers of "zeros" in the matrix elements. That is, interestingly enough, exactly what we have here. The vast majority of entries in our method-map would contain a zero, since most methods would not exist on most classes. Zero, as it turns out, would neatly map to our good friend "method_missing", and so dispatching to a method on a target class that doesn't implement it would continue to work as it does today. Even better, method_missing dispatch would be just as fast as a regular method dispatch, excluding the actual method_missing implementation code, of course.

So then, there it is...a potential plan for fast dynamic method invocation in JRuby (and perhaps in Rubinius, if Evan agrees the idea has merit). Any of you out there heard of such a thing? Is this an old idea? Are there problems with it? Share!

Holiday Fun: Interpretation, Loading, Dispatching Work

This week (what's left of it) I'm spending on performance again. It's officially a holiday for Sun, so I'm not technically on the clock. I could even sleep the entire week and pick things back up on Tuesday.

Yeah, right.

One of the oddities of working on JRuby while working at Sun stems from the fact that I was working on JRuby for fun for the past two years. Now that this is the full-time job, it's even harder to pull myself away from it. I thought perhaps making JRuby my job might take away some of the attraction. In reality, it's just made a good thing better, since I can spend long hours on the hard problems I would never have tackled before.

So instead of stopping work completely, I'm shifting gears a bit. Instead of the heavy Rails focus we've had over the past month, I'm hitting other "fun" stuff instead. Compilation, interpretation performance, and so on. We know there's still lots of fruit to be plucked from the performance tree, and of course any additional work/research on compilation will help that eventual goal.

Compiler Refactoring Underway

As far as compilation goes, I've started committing a refactored compiler; it separates AST-node-walking from code generation, so the backend could be swapped out with a YARV generator or future compiler revisions. I think that's needed to allow the compiler and the AST to evolve independently, since I believe there will be many possible compile targets and potentially many AST changes in the future. Nothing too fancy there, but it's a bit more readable so hopefully others can also contribute.

Interpreter Enhancements and Fixes

On the interpretation front, there are a few items I've been working on.

1. Speeding up method invocation and block management (committed)

Ruby's AST represents method calls that take a block by putting the block first in the AST. You encounter an "iter node" that points at the "call" with which it is associated. So the typical way you evaluate those nodes is to create the block object and then visit the call. Unfortunately, the call itself may lead to other calls with their own blocks, for evaluating the arguments or receiver.

The end result of this node ordering is that every time a method is called, the evaluation of its args and receiver has to juggle the current "block available" status around, so the block doesn't get consumed before it's needed. Because the block gets created early, we have to push it and the "block available" status onto a stack. This is the primary reason the block logic in the interpreter and ThreadContext is so complicated.

An example would help illustrate what's happening. For the following code:

foo("hello") { 1 }
The parser generates the following hierarchy of AST nodes:
IterNode[]        <= this is the block
NewlineNode[]
FixnumNode[] 1 <= this is the Fixnum 1 inside the block
FCallNode[] foo <= this is the call to foo
ArrayNode[]: {StrNode[]}
StrNode[]"hello"
The node associated with the block is encountered first, so we construct the block then. We move on to the "foo" call, and the block is consumed. No problem, right? However, here's a more complicated example that illustrates the trouble with this AST ordering:
foo { 1 }.bar { 2 }.baz(hello { 3 }) { 4 }
Here things are a bit more interesting. The parser produces the following output:
IterNode[]            <= the "4" block
NewlineNode[]
FixnumNode[] 4
CallNode[] baz
IterNode[] <= the "2" block
NewlineNode[]
FixnumNode[] 2
CallNode[] bar
IterNode[] <= the "1" block
NewlineNode[]
FixnumNode[] 1
FCallNode[] foo
ArrayNode[]: {IterNode[]}
IterNode[] <= the "3" block
NewlineNode[]
FixnumNode[] 3
FCallNode[] hello
So what actually happens here? First the "4" block is encountered, instantiated, and pushed onto the block stack. Then we proceed to the "baz" call. Unfortunately, the "baz" call has both a receiver and arguments, so we have to hide the "4" block to prevent it being consumed. We proceed to evaluate the receiver for "baz", encountering the "2" block. The "2" block is instantiated and pushed down, and we move on to the "bar" call. The "bar" call has another receiver; we evaluate that, encountering the "1" block and the "foo" call it's associated with. The "foo" call consumes the "1" block and returns a receiver for "bar". The "bar" call consumes the "2" block and returns a receiver for "baz". Now the "baz" call has to evaluate its arguments, so the "3" block is created and consumed by the "hello" call. Finally, with a receiver and args, we can call "baz" and consume the "4" block.

Confused? Me too. Why would you order it this way? Perhaps it's to ease parsing, or perhaps there's some reason I don't know. However, I believe the following ordering is much simpler (and I know it makes interpretation easier):

(extraneous nodes omitted)
CallNode[] baz
CallNode[] bar <= the receiver for "baz", a call to "bar"
FCallNode[] foo <= the receiver for "bar", a call to "foo"
IterNode[] <= the "1" block associated with "foo"
IterNode[] <= the "2" block associated with "bar"
ArrayNode[] <= args to "baz"
FCallNode[] hello <= the call to "hello"
IterNode[] <= ...and its "3" block
IterNode[] <= finally the "4" block
The advantages here should be obvious. We encounter the blocks in order, so there's no stack juggling involved. Because there's no stack juggling, we don't have to "hide" blocks as we evaluate receivers and arguments. Finally, because we know we'll only encounter blocks for methods that require them, there's no additional overhead for methods that don't need blocks. It's a good change, and I would love to understand why Ruby uses the more complicated AST structure instead of this.

I have already committed a change to reorder the way these AST nodes are handled. The AST itself is unchanged, but the visit to a given block (IterNode) just sets that node into the associated call and proceeds. The calls themselves are now responsible for creating an associated block (if necessary)...*after* receiver and args have been dealt with. This means two things: calls that don't accept blocks don't pay any block-manipulation penalty; and calls with peripheral blocks (for finding args or receivers) don't pay any block-manipulation penalty either. Only the calls that need blocks have to deal with them.

Eventually this will become an AST change, but this short-term fix resolves 90% of the interpreter goofiness right now. This change will also eventually mean the iter stack (and potentially the block stack) disappear too. Huzzah!

2. LoadService fixes, enhancements, and optimizations (committed)

LoadService cleanup and improvements are well under way. LoadService is responsible for "load" and "require" calls and does all the searching for files and management of loaded extensions and libraries. Unfortunately the existing heuristic was both broken and terribly inefficient.

Ruby's 'load' behavior is easy enough...just look for the exact file and execute it in the current runtime. Ruby's 'require' however has a bit more magic to it.

If you specify a full filename to 'require' it will use that filename to load either a source file or an extension, depending on whether you specify ".rb" or ".[so|o|dll|etc..]". If you do not specify an extension, it will search for .rb, .so, etc in turn until it finds something, If it finds nothing, that's a load error. Here's the "ri" doc for MRI's "require":
--------------------------------------------------------- Kernel#require
require(string) => true or false
------------------------------------------------------------------------
Ruby tries to load the library named _string_, returning +true+ if
successful. If the filename does not resolve to an absolute path,
it will be searched for in the directories listed in +$:+. If the
file has the extension ``.rb'', it is loaded as a source file; if
the extension is ``.so'', ``.o'', or ``.dll'', or whatever the
default shared library extension is on the current platform, Ruby
loads the shared library as a Ruby extension. Otherwise, Ruby tries
adding ``.rb'', ``.so'', and so on to the name. The name of the
loaded feature is added to the array in +$"+. A feature will not be
loaded if it's name already appears in +$"+. However, the file name
is not converted to an absolute path, so that ``+require
'a';require './a'+'' will load +a.rb+ twice.
There's also the issue of what paths to search. Ruby searches the current directory first, followed by custom load paths, site_ruby dirs, and ruby/1.8 dirs. This allows a number of mechanisms for overriding more general locations with more specific ones for particular uses.

JRuby adds a new wrinkle here: classloader resources. JRuby supports JARing up source files and loading them through Java's classloader mechanisms. This is how the JRuby applet and the "complete" JRuby JAR work: they simply include all Ruby source into the archive. So then for us the classloader/classpath represents an additional path to be searched.

The primary problem with JRuby's load heuristic is that it ended up searching the classloader far too frequently; and in many cases it searched it multiple times for files that could not exist, such as for complete absolute paths (starting with '/', which *never* works for classloader resources). A second problem with the heuristic is that it would try all locations for all extensions, so for example it would search for xxx.rb everywhere possible, then xxx.rb.ast.ser (our serialized AST format) everywhere possible, then xxx.so (used internally for extensions...though that may change), and so on. The result of this is that any extensions or serialized scripts went through the full monty of searches before being found on a second or third pass.

These two issues were compounded when running JRuby with a very large classpath. Because classloader resources can be expensive to search, our loading became linearly slower in relation to the number of JARs (or perhaps the size of jars) included into the JVM. More JARs, slower classloader resource searching, slower startup.

The fix for these issues is twofold: search a given load location for all filename extensions before moving on, and only search the classloader as a last resort for filenames likely to be found there. It's fairly simple to explain, but the LoadService code had been endlessly hacked and rejiggered over the years. The cleanup was 90% of the battle; the fixes were considerably easier.

A final issue with LoadService, which is now fixed, was that it allowed require to include files with no extensions at all. This is not correct Ruby behavior, and so now those files can't be loaded. This actually caused a bug a long time ago where the extension-free "rake" startup script was being loaded before the "rake.rb" library file it tried to locate. The result was an eventual stack overflow as the file tried to continually load itself. Goofy behavior we should never see again.

3. Speeding up dynamic dispatch

I'm also doing some experimental work to speed up dynamic dispatch. Currently, methods are located by name, looking up a callable object out of a big hash on a per-class basis. This works reasonably well, and there are caches to speed the process, but with all the recent performance work this search has started to become the new bottleneck.

The eventual callable objects looked up have another flaw: they make Hotspot optimization more difficult. Because they're behind an ICallable interface, because they have multiple levels of logic as well as pre/post-call setup and teardown, and because there are many different implementations of ICallable, they end up slowing execution down significantly.

Hotspot is really an amazing piece of work. For the vast majority of Java code, it's able to unroll loops, inline invocations, dynamically optimize conditionals and switches, and generally improve the speed of code by a drastic amount. When running in "server" mode, where Hotspot lets code run longer in interpreted mode before optimizing it, even greater improvements can be seen. For example, A fib benchmark--recursive and iterative--under Java 6 client and server VMs:

(best times shown)
client recursive:
8.551000

client iterative:
17.995000

server recursive:
5.008000

server iterative:
13.191000

MRI recursive:
1.670000

MRI iterative:
16.964403
These numbers aren't bad, really. We even beat MRI for this trivial benchmark when we start hitting Bignums heavily (Java's BigInteger implementation is quite a bit faster than Ruby's). However, we can certainly do better.

There are two experimental changes I'be been working on. The first eliminates the pre/post method setup for core libraries when that setup is not necessary. I call it "fast invocation", and it's applicable to a large majority of core class methods. For example, with just fast invocation, the recursive fib numbers above drop to the sub-5s range. This change is perfectly safe, since it's just eliminating interpreter overhead that would otherwise be wasted cycles. You can expect to see it included in JRuby soon.

The second change is the holy grail of dynamic invocation: eliminate, to the greatest extent possible, the overhead of looking up and dispatching to a given method. In short, make it as close to a simple static dispatch as possible. This is where the real speed gains in JRuby will start to show up.

I have some experimental code right now, focused on the fib benchmark, that is both safe and drastically improves performance. It's sub-par code at the moment, but it does produce results like this:
recursive before:
5.008000

recursive after:
3.864000
Now of course, this is still interpreted. The same change when applied to my experimental Ruby compiler produces a much more drastic effect:
compiled recursive after:
1.550100
Now we start to see the value of eliminating dynamic-dispatch overhead. This is actually *faster* than Ruby's recursive fib, a feat that hasn't been accomplished by JRuby at any time in the past.

The trick to this is fairly simple. For common core methods which are known to be simple Java code, such as for Fixnum's +, -, and < implementations, I provide integer IDs. Within the Fixnum implementation there's a new implementation of "callMethod", our dynamic-dispatcher, which switches on these IDs. For methods it knows, such as the aforementioned +, -, and <, it dispatches directly to op_plus, op_minus, or op_lt, the Java implementations. This skips the lookup phase, the ICallable implementation, the ThreadContext manipulation, and the pre/post-method setup code completely. It's also perfectly safe, again, because all those pieces only waste cycles for simple methods like this.

Now one problem with a simple approach like this is that if you redefine Fixnum#+, that change won't be picked up. The simple Fixnum callMethod won't ever try a method search for methods it knows can be fast-dispatched. I resolved this in my experimental code by adding a "clean" flag to the Fixnum class. If any any point after its initial definition the Fixnum class becomes "dirty", e.g. if you add or redefine a method, the old, slow dispatch will come back into play. My simple version is too coarse-grained, killing fast dispatching for all methods if any of them are changed, but the principal is sound. I'm going to be exploring this the rest of the week, trying to find a more complete solution...but some good things are around the corner.

--

All told, it's been a productive few days since JavaPolis. I'm hoping to write up a JavaPolis recap soon, but I'm keen to use this holiday time to get some cool stuff done. You'll hear more after the first of the year...hopefully with committed code and additional benchmarks against JRuby trunk :)

Sunday, December 10, 2006

Two JRuby Talks at JP: What's the Difference

There will be two JRuby talks at JavaPolis.

The first is part of the "University" sessions tomorrow. We're the middle hour of a three-hour bit on scripting languages for the JVM. During that session we're going to be focusing on practicalities like building a simple app, using IRB, and getting a basic Rails app scaffolded and working. We'll talk a bit about JRuby futures, but we won't demo any of the newer, crazier things like NetBeans or GlassFish.

The second talk is on Wednesday, and will cover some of the same material as the first but hopefully have different demos. We'll show the Agile Web Development with Rails v2's "Depot" application running in JRuby under WEBrick (hopefully GlassFish if some issues are worked out, but likely not), we'll demo some NetBeans Ruby support (try to fit in as much as Tor can finish by Tuesday afternoon), and if there's time to implement some of it, we'll do Rails + JavaEE stuff as well (maybe, maybe not, given that we only have two days left).

Both talks should be useful, but there's no way we can avoid some duplication, especially since many folks will only attend one of the talks.

I hope to see plenty of people at both, however :)

JRuby JavaPolis Meetup

Another conference, another meetup!

JRuby JavaPolis Meetup

Join us! We haven't decided on a location, and we're taking suggestions. These little meetups have been great fun in the past, so don't be shy and feel free to grab us after one of our talks to help figure out a good place to go.

Monday, December 04, 2006

JRuby IRB Applet Revisited

Damian Steer, regular JRuby contributor, has taken the IRB applet and run with it. He's gotten readline working (history, line editing, tab-completion), added some fonts and colors to differentiate things, and even put in an intellisense-like menu for tab completion of method names.



Very cool stuff. Keep in mind also that this could be embedded in any app (like an IDE) to provide a really nice looking interactive console. Thanks Damian!

Saturday, December 02, 2006

Another Step Toward Rails WAR Files

Ashish Sahni has posted instructions for packaging a Rails app as a WAR on his blog, based on the work of a number of JRuby community members. I ran through his instructions, and have only two modifications to make:

  1. When installing Rails, you still probably want to pass --no-ri --no-rdoc since rdoc generation is still far too slow.
  2. Because JRuby has a classloading bug (not loading from context classloader) you'll need to put the rails-integration JAR in glassfish/lib.
I was only able to get the "properties" page to display, at rails/info/properties. A separate page that used ActiveRecord didn't successfully execute...but it was close enough to taste.

So then, there's two important points you should get out of this:

Rails in a WAR file will happen...soon

This has always been our goal, with every improvement and fix we made to JRuby. The holy grail of Rails deployment would be to "zip it up and deploy it" like Java app developers can do with WAR files. And that goal is just around the corner, thanks to the JRuby community. That leads me to the second point:

The JRuby community is growing fast and doing great things

This is especially exciting to me. Tom and I are only two folks, even with full-time license to work on JRuby and related projects. The only way Ruby can be successful on the JVM is with community cooperation, and the work toward WAR deployment exemplifies what can happen.

I'd like to thank the folks on the JRuby-Extras project who have been working on this: Robert Egglestone, Chris Nelson, Fausto Lelli, and also Ashish Sahni at Sun who put together a great walkthrough.

Excellent work.

Wednesday, November 29, 2006

NetBeans + Ruby = Awesome

Tor Norbye is a programming machine on par with the legendary Ola of Bini. He's the one-man force working on NetBeans Ruby support, and his progress has been epic. Here's his latest screenshot and a short blurb about it:

NetBeans + Ruby = True

In just over two months' time, Tor seems to be (in my opinion) on the verge of eclipsing every other Ruby editor/IDE out there. I've been using development builds of his stuff and it's really superb. A few features I've missed before that are now rapidly maturing in NB+RB:

  • Highlight usages: not genius, but smarter than dumb-as-a-post right now; great for local and block vars, getting smarter for methods
  • Clickable methods and variables: limited to method-scope for variables or file-scope for methods, but coming along very quickly...and even those limited scopes are way better than nothing at all. I don't know how many times I've wanted to Ctrl-Click a method in some giant Ruby file and go straight to the method def. Awe-some.
  • Inline refactoring: I've demoed this a couple times, but for local and block vars you can do inline renaming...essentially renaming all instances of the variable at the same time. It's really nice, looks cool, and represents the tip of the iceberg for refactoring capabilities.
There's plenty of other stuff that's cool, like the Navigator view of a file (shows classes, modules, methods, of currently-selected file), AST view (great for us Ruby language hackers that like to see the actual structure of things), and more.

Things are definitely shaping up nice for next-generation Ruby IDEs.

Sunday, November 19, 2006

Using JRuby's "complete" JAR for OS X App Bundles

Now this is really cool. Tony Hursh, commenter on the previous "Advanced Rails Deployment" post, put together an OS X Application Bundle template that allows you to use the JRuby "complete" JAR file as the base of a typical OS X app. What does that mean? That means you just toss the complete JAR into this template, code up some Ruby code, and have a nice dock icon and menu bar like any other app. You can ship the entire app as a bundle, with JRuby as the built-in Ruby interpreter. Awesome.

Some pics showing the menu working like you'd expect and the JRuby logo as a dock icon:





Check out Tony's JRuby OS X App Bundle walkthrough to see for your self. It's a really outstanding application of JRuby's "Ruby-in-a-JAR" support.

Eclipse to NetBeans: Quick Outline Module

I'm trying to be 100% NetBeans these days, and I'll be documenting tips and tricks as I learn them. Hopefully others going through the same exercise will find these tips and they'll help make the transition smoother.

Why am I making this move, you ask? Well, of course there's the whole fact that I work for Sun, but that's not the primary motivation; if a tool doesn't accomplish what I want, I'm not going to use it. But NetBeans has made amazing strides this past year. It's now not only better-looking and faster than Eclipse, it also includes includes a much, much larger set of functionality in the base download. And that download? 30-60MB smaller than Eclipse 3.2. That's pretty amazing.

Anyway, one feature I sorely missed was the "Quick Outline" in Eclipse, where Command-O (or Ctrl-O) brings up a fast search for members of the current class. It's pretty darn useful, and I really, really missed it.

However, Sandip Chitale to the rescue. He's created a "Java File Structure" module, which exactly duplicates the Quick Outline functionality. Thank goodness!

Read Sandip's post to install the NetBeans Quick Outline module (Java File Structure) and try it for yourself. He's also got a quick Java class hierarchy module that looks great, but it's not something I use very often. Perhaps I will use it more now.

Oh, and you can find information about the modules in Help after they're installed, but to save you some confusion: The shortcut for File Structure is initially Cmd-Shift-S (Ctrl-Shift-S) and for Hierarchy is Cmd-Shift-H (Ctrl-Shift-H).

One more NetBeans annoyance down the drain!

Friday, November 17, 2006

Advanced Rails Deployment with JRuby

There's a lot of work going on right now focusing on various mechanisms for deploying JRuby-based apps. This article will summarize some of the work happening and why it's really, really important for the Ruby world.

Ruby-in-a-JAR

First, a little sideline into general Ruby embeddability work.

Over the past few days I've made modifications to enable running a Ruby app completely out of a single JRuby JAR file (Java ARchive). The major changes required were:

  1. Add jarjar to the project to combine all dependency jars into a single archive, including jline (readline support), asm (compiler, other stuff in the future), bsf (scripting API), jvyaml and plaincharset (Ola's JvYAML library and supporting charset lib).
  2. Add an Ant task to build the "complete" jar and include all Ruby standard libraries in the same archive.
  3. Somewhat unrelated, a small patch for irb/init.rb to allow it to fail gracefully if it can't load locale-specific files from the filesystem.
So by running "ant jar-complete" you get out a single JAR file that contains a complete, working Ruby interpreter plus stdlib. For example:
~ $ java -jar jruby-complete.jar -e "puts 'Hello, Ruby-in-a-JAR!'"
Hello, Ruby-in-a-JAR!
~ $ java -jar jruby-complete.jar -rirb -e "IRB.start"
irb(main):001:0>
Of course, you don't have to take my word for it. I've uploaded a copy of this jar for you to try yourself. The full archive is about 2900kb. That's suitable for embedding in just about any application, and in fact I used a stripped-down 1600kb version for the JRuby Applet. Note: this is JRuby trunk code and mostly experimental...but that's what makes it so fun :)

Ruby-in-a-JAR - The ultimate in Ruby interpreter portability

A Better Deployment for Rails

Now the main course: several folks have been working on several exciting deployment scenarios for JRuby on Rails apps.

The current "best option" for deploying Rails apps into production generally involves the HTTP front-end Mongrel. Although Mongrel is largely written in Ruby, it is very fast, largely because of its native C component for HTTP request parsing. It's also considerably more secure than CGI-based options, largely because of creator Zed Shaw's attention to detail. The typical Rails app will be deployed as a "pack of Mongrels", where the number of desired concurrent requests is multiplied by the number of independent Rails apps to determine a total number of processes. These processes must be managed, monitored, and respawned as appropriate, but the result is a fairly stable and scalable deployment model.

However with JRuby, there will soon be a better option. I previously reported about TAKAI Naoto's efforts to deploy Rails behind an AsyncWeb front-end, showing tremendous performance improvements over a WEBrick-based deployment. Naoto-san has now taken things to the next level: Rails deployment under GlassFish.

The potential here should be obvious. GlassFish, like other Java EE application servers, is extremely good at scaling up many concurrent requests across many independent applications; so good that many organizations deploy only a single appserver-per-machine and stuff it full of applications to serve. That means a single server, a single process to manage. GlassFish also supports clustering, which means you'll be able to hit the deploy button once and have your n-server cluster instantly start serving up Rails. But there's one last area that trumps all the rest:

That single app server can handle as many concurrent requests across as many independent Rails apps as you desire, scaling them across all the CPU cores you can throw at it.

That's right...no more N * M process management, no more zombie processes, no more immature tooling to manage all those servers and all those deployments. One tool, one server process, no headaches.

That's an extremely bright future, and we're almost there.

What Next?

Naoto-san is not the only one working on JRuby on Rails deployment options. There are a number of folks in the JRuby community approaching the same goal from different directions, using innovative techniques like servlets implemented in Ruby and Spring-based service wiring. The JRuby community sees the potential here and things are moving very quickly.

My work on Ruby-in-a-JAR will also play directly into this. Currently most deployment scenarios require Rails app files to remain "loose" on the filesystem, as with the current standard deployment model. However it won't be long before you can zip up your Rails app into a WAR file (Web ARchive) and deploy it lock, stock, and barrel to as many servers as you want.

These efforts combined will create, in my opinion, the most manageable, scalable, powerful Rails deployment model yet available...and it's just around the corner.

We've also launched into Rails compatibility work in earnest. I've created a wiki page on JRuby support for Rails that details the results of running Rails' own test cases. Long story short: we're looking pretty damn good.

JRuby on Rails is in the home stretch. And we're covering ground very quickly.

Wednesday, November 15, 2006

Jython: Alive and Well (and looking for love)

I thought I'd make a diversion from my usual JRuby activities this weekend and ping the Jython dev list. I'd been lurking for a month or two, seeing almost no activity other than the occasional email, bug report, or request for help. There were perhaps 5 emails in the last month. Not good.

So I reached out to see if anyone was actually listening and to offer whatever support I can provide. As it turns out, there's still a dev team and a user community on the lists, though development has slowed almost to a standstill. There hasn't been a release in years, and Jython is currently about 2x slower than current CPython, while only supporting Python 2.1 semantics in the widely-available release.

But there's a light. The existing team and users are very much interested in getting Jython going again, and from my examination of the code it shouldn't be too difficult for new devs to get involved. Jython also has a pretty good story for compilation to Java bytecode--better than JRuby currently--so it has a strong base to start from.

So here's my request to folks reading this post: If you're a Python fan and a Java developer, now's your time to show devotion to both communities. The Jython guys could use some help, and the project could certainly use some new blood. It doesn't matter to me if you're not a Ruby fan, or heck, if Jython ends up reaching CPython 2.5 compatibility before we get a JRuby 1.0 release out. I'd just really like the JVM Python story to have a happy ending...and I think Jython's long slumber needs to come to an end.

Jython is available on SourceForge, and the existing dev team are friendly, enthusiastic folks. Post this entry to your blogs, send it to your friends, print up flyers and hand them out at your local Python or Java User Group meetings. Jython needs some love, and now is the age of dynlangs for the JVM. Stop by and lend them a hand...I have.

Ruby for the Web? Check!

Well friends, it's time for another episode of "Impossible Or Not!" Today's contender is Ruby in the browser, long desired but never achieved. There are front-ends to Ruby services, delicious Ruby-JavaScript libraries, and of course the ever-popular Ruby on Rails web framework. However, developers have been clamoring for something more.

IRB in an Applet

A long, long time ago in a web far away, there was born a bright-eyed new child named Java. Java found its first public uses in flashing, twirling, annoying buttons called Applets. It was also a bit slow, having just been released into the world. So the big bad public said "Java is slow and only useful for annoying buttons!" And so Java was branded the "slow annoying button" language.

But Java has grown up. It hid away from the public eye, dwelling in dark, dank servers and enterprises. It learned the value of five nines uptime and horizontal scalability. All the while, it improved its public face, preparing for a return home to its birthplace on the web.

Now Java has grown up. It has learned how to appease the enterprise gods while presenting itself to the web in beautiful, performant glory. And it has a new friend: Ruby.

Yes, JRuby can run in an applet. No, it's not that hard. No, the archive doesn't have to be this big (the applet above is about a 1.6MB JAR file, but it includes stuff it doesn't need). Yes, this means you could start writing stuff for web pages in Ruby. No, I'm not kidding.

Yes, Virginia, there is a Santa Claus.

Updated: I fixed the issues under Windows, so it should work for those of you that reported errors. Thanks for the heads up!

Tuesday, November 14, 2006

Sun Turning Heads

Thijs van der Vossen posts an interesting perspective on recommending Sun and how his opinion of the company has been changed by recent events. It seems that the truth is really getting out: Sun "gets it" and is rising again as a great innovator.

It's a great time to be here.

Monday, November 13, 2006

Java Open-Sourced Under GPL; Sun Shines Brighter

Yes, it's official. News is already starting to pop up around the net about Java's open-sourcing and especially about the choice of license: GPLv2. I must admit my jaw dropped when I first heard about this a couple weeks ago, and it was a hard secret to sit on. Not only is it open source...it's open source using the most vigorously open license out there.

I think the GPL is a great choice for Java. Not only will it be fully compatible with the vast range of GPLed software, but any folks hoping to release their own versions will be compelled to make their changes available as source. Say what you like about the GPL and its "virulence" or its "tainting", but for an open development platform about to explode in the open-source world, it's hard to say what license would be a better choice.

I'm proud to be at Sun surrounded by thought-leaders smart enough to see this is the right thing to do. Sun is BACK, baby!

Tune in for Jonathan Schwartz's and Rich Green's webcast at 9:30PT to get all the details about what's being opened up when.

Saturday, November 11, 2006

McGovern Likes JRuby

I'm not entirely sure how to feel about this one. Apparently James McGovern believes that "the Ruby community at large should drop their current approach and embrace the JRuby stuff." He notes that Jeff Sutherland recommended the same thing for Smalltalk years ago, to save it from being left behind.

Now I haven't generally agreed with James McGovern in the past. His hilarious post about how "Ruby isn't ready for the enterprise" was pretty ill-informed, though perhaps well-meaning. My primary issue with that post was that there's nothing about Ruby--the language, libraries and apps--that would prevent it from being perfectly suited to enterprise development. There are issues with the implementation, certainly, but I don't believe that Ruby necessarily has to equate with the C version. Enter JRuby...

JRuby is Ruby. It looks like Ruby, it acts like Ruby, it walks like a Ruby and talks like a Ruby. We aim for it to run Ruby apps and libraries; we hope for it to be as close to 100% compatible as possible. The fact that some of it is written in Java or that it runs on the VM-formerly-known-as-Java is wholely irrelevant; JRuby is Ruby.

I think it's been pretty well proven that the Java VM is well-suited for enterprise development. The majority of enterprise apps out there today are written for or being written for the JVM, and Sun's had whole teams of folks making the JVM run as well as possible for exactly those scenarios. There's no doubt about the JVM's enterprise capability.

So it should follow that Ruby on the JVM, in the form of JRuby, would inherit much of that enterprise-readiness. Does that mean McGovern is right? Should the Ruby community abandon YARV and Ruby 2.0 and the C impl for greener pastures (or in the case of threading, less green pastures)?

No. To do such a thing would be absurd. And there's a simple reason for this: Not everyone wants to run a full-featured VM.

Ruby in its current form has served its users well. It's an outstanding administrative language, great for text processing, network tickling, application scripting. It's even proven itself for small to medium-sized web applications using numerous frameworks, from Camping to Rails. Even more, it has shown its capability for targeted "enterprisey" tasks, like tying together services or generating code and components to be consumed by other systems. Ruby has done its job admirably, and that job isn't going anywhere.

I will fully admit that JRuby in its current form is probably not ideal for heavy command-line use. The minimal runtime that the C implementation starts up is a better fit for quick hit scripts, there's no doubt about that. And for many web deployment scenarios, the C implementation works suitably well, fulfilling its responsibilities without issue. Where McGovern is right is that JRuby is better suited to much larger applications, where scaling across multiple CPUs or multiple machines is an absolute necessity; where resources are quickly consumed by thousands of independent processes; where monitoring, management, and deployment needs can't be addressed by current pure Ruby or C-based options. In short, JRuby fills the medium to large application realm where Ruby has trouble venturing.

Of course I'd love to see JRuby become the best Ruby implementation. What would be the point of working on JRuby if that weren't an ultimate goal? And of course I have a love for Java and the JVM; they've proven themselves in my eyes, and continue to amaze me. But I want JRuby to be part of a larger Ruby world, where programmers run through flower-covered pastures holding hands, objects sing and swirl through the heavens, classes condense, evaporate, and recombine like vapor. Where programming is "fun", like it was when I started BASIC on my Atari 400 25 years ago. Where our time spent writing software produces results, rather than more problems.

None of those things requires Java or the JVM...they just require cooperation within the community and a desire to see Ruby succeed on all fronts. The question that remains, I believe, is this:

Are you with me?

Progress on WAR, RMagick, OpenSSL, Mutex Fronts

The JRuby community continues to pick up steam!

A number of JRubyists have recently started actively looking at the problem of deploying Rails apps as a WAR file. Some have working prototypes as well. However the most interesting development is that a number of them have joined the jruby-extras project on RubyForge to combine their efforts. I'll be helping to oversee their progress, but this is a perfect example of a community-driven project. I don't doubt they'll make great progress.

RMagick will soon have a full-featured equivalent for JRuby. Tom Palmer has been working on a Java-based RMagick for some time, and now has a version of his RMagickJr that can render some basic Gruff Graphs. He's been in communication with the RMagick creator, and it's likely that we'll start to see gems available soon. Tom's work will help ensure that Rails apps using RMagick for image processing can work seamlessly under JRuby as well. RMagickJr is also hosted in the jruby-extras project.

Ola Bini has been continuing his quest to bring the openssl library to JRuby. He says he's getting very close to having a working library, and it's been a long, hard road. Full support for openssl will mean all Ruby libraries that depend on it will work without modification on JRuby. It's quite an effort, and Ola deserves a lot of credit for making it happen.

At RubyConf 2006, Matz and Koichi made the announcement that Thread.critical was very likely to go away in Ruby 1.9.1/2.0. The reasons for this are simple, and well-known to the JRuby project: Thread.critical is incompatible with native threading. My own implementations of Thread.critical have ranged from a very strict version which frequently deadlocked to the current version which only enforces critical sections in a very loose sense. I am very pleased to hear that critical will go away, but that doesn't help us now. However, there's hope. MenTaLguY has recently taken on the challenge of implementing a fast Mutex for both the C and Java versions of Ruby (MRI and JRuby, respectively). As I understand it, the current C implementation he's built exceeds even low-level Thread.critical performance, and we both agree that a Java version should be extremely easy to construct using Java's built-in synchronization capabilities. A fast Mutex is the first step toward moving people off Thread.critical...and saving me doing yet another doomed reimplementation of it in JRuby.

Other news:

Tom and I spoke with some folks from the HotSpot VM team last night, and it was an extremely helpful discussion. We talked about Ruby's language design and quirkier features, the future of dynlangs on the VM, compilation and optimization strategies for dynlangs, and the current roadmap for JRuby development. Bottom line: everything we're doing is right, and if we keep on this course we'll rapidly approach their notion of an optimal Ruby implementation. We also agreed there's very little about Ruby that couldn't be compiled straight down to Java code. It was great vindication to hear that our "best guess" strategies for slowly redesigning, refactoring, and compiling JRuby are all on the right track. It was also great to hear that our confidence in the JVM has not been misplaced: it IS an excellent VM for dynlangs, and JRuby should eventually perform extremely well. The future of Ruby on the JVM is looking great.

I will be presenting JRuby again today at the Twin Cities Code Camp, as one of the few Java-based presentations (the rest being primarily .NET-related). I guess that's all there is to say about that...it's going to be a condensed version of the Gateway JUG presentation with fewer walkthroughs and a much shorter overall time.

Tom and I are also scheduling our trip to Europe in December. We'll be in Prague from the 5th to the 9th to meet with the NetBeans development team; in Antwerp the following week for JavaPolis; and in Rotterdam on the 19th for my JRuby presentation at Finalist. It remains to be seen if I'll spend some of the holidays in Europe or if my wife will join me, but if you'd like to propose any speaking engagements that could keep me in the Old Country, certainly let me know :)

Thursday, November 09, 2006

Poll on Favorite Java Scripting Lang

There's an interesting little poll here on favorite Java-based scripting lang:

What is your favorite Java based scripting language?

It doesn't look like the poll has been hit too hard, but JRuby seems to score much better than I would have expected, given our relatively recent entry into the public eye. Groovy has garnered about twice as many votes, but of course it's been very public and available for several years.

Also interesting are the poor scores for Jython and Rhino. Jython is a great implementation, but it's pretty far behind at this point. I wish there were more resources to pour into it...perhaps soon. Rhino is also interesting...only four votes so far, even though it's really fast and being included in Java 6.

BTW, please don't go stuffing the ballots for JRuby or anything. I just figured I'd get this poll some exposure to see how things play out. It already looks really good for JRuby, and if you want to vote for Groovy or any of the others, be my guest.

Wednesday, November 08, 2006

Java SE 6 Passes Final JCP Approval

Huzzah! Java 6 has been approved by the JCP. The final results on JSR 270 show that the voting was 13 for, none opposed, with three not voting. As I understand it, this is the final step toward officially releasing Java 6 to the masses.

Java 6 is really an incredible piece of software. Once again the Sun engineers have boosted performance--in some JRuby benchmarks by as much as 20%. Beyond that, they've include the first round of native platform scripting support in the form of JSR 223, a scripting API intended to replace and improve upon the Bean Scripting Framework. Java 6 also includes native support for Javascript in the form of Rhino, just about the fastest mainstream JavaScript engine available.

Honestly, give Java 6 a shot. I've been using it constantly because of the performance gains, and JRuby support for 223 is already available. This is the first Java release during my time at Sun, and I'm extremely proud to be a part of this team.

Gateway JUG: Such Fun!

Last night I gave my 2+ hour talk on Ruby, JRuby, and JRuby on Rails to the Gateway JUG in St Louis, and it seems to have been a resounding success. We had extremely good attendance, pushing 50 folks from what I could tell, and everyone seemed to be very excited for the possibilities of Ruby on the Java platform. Many folks told me they were going to be looking into Ruby and Rails for future development, and others promised to contribute to the project however possible.

It was a great experiment for me to do this presentation, primarily because it was one long demo. There were a few slides to bridge things together, but ultimately I spent two hours typing into IRB, vi, and bash to demonstrate Ruby, JRuby, and building and running a simple JRuby on Rails app. There were a few glitches (I forgot a few metaprogramming methods, and my new migration initially failed because I ran it on an already-migrated database) but I managed to recover from everything and get all my demos across. I think the live walkthroughs coupled with a very enthusiastic and interactive crowd made a fun presentation for folks in attendance.

Afterwards I had a few beers with the locals and we shared our war stories about Java application development and excitement for a Ruby-filled future. Judging by their reactions to the talk and the stories they related, I think this JRuby thing is poised to really take off.

I've uploaded my Gateway JUG JRuby slides so you can see and share them, and Alex Miller from BEA took a transcript of the JRuby live walkthroughs.

Gateway JUG JRuby Presentation (Opendoc)
Gateway JUG JRuby Presentation (PDF)

Friday, November 03, 2006

Java EE Architect? Need a Job?

My former employer, Ventera Corporation, is still looking to fill my old position. I was their lead/senior Java EE architect, in charge of development of EE applications for their USDA Food and Nutrition Service contracts. I worked at FNS's downtown Minneapolis office, though a large part of the development team resides in Virginia at Ventera HQ.

Update: BTW, I'll be interviewing you, if that's any motivation for or against you applying...

If you're interested, contact Christina Pritchett at Ventera:

Email: cpritchett@ventera.com
Phone: 703.442.5223

And here's the job description Christina gave me...exciting fun for a Friday afternoon:

Senior Java Developer/Team Lead/Architect
Travel: 10% (back to our HDQA in Mclean, VA)
On-boarding: first few weeks will need to be spent in our McLean office

This individual will work on a development team for a large government client to develop, deliver and support a web-based application from design through implementation.

Responsibilities:

  • Perform as a team lead – mentor junior staff and serve as the primary point of contact for the customer
  • Perform as system architect role in the development of large-scale applications
  • Develop prototypes from business and technical requirements/use case documents
  • Conduct unit and integration testing
  • Provide source code documentation and write reusable code; Maintain existing code base and component library
  • Develop clear and concise documentation for work assignments
  • Meet critical project timeframes
  • Follow coding/design standards and J2EE Design Patterns
  • Maintain application by implementing client enhancements as requested
Qualifications:
  • 7+ years of relevant work experience - must have a minimum of 3 years as a tech lead and system architect (i.e. system design, performance tuning, and system prototyping & maintenance)
  • Strong knowledge of Eclipse 3.1, WebLogic Platform 8.1, Hibernate, Spring, CVS (including branch based release management), Bugzilla (admin/server side), and Apache web server
  • Ability to configure WebLogic server/cluster and troubleshoot issues related to WebLogic clusters
  • Extensive knowledge about best practices across multiple disciplines including requirements analysis, architectural design, QA, project management, and configuration management
  • Experience with Enterprise Java Beans development and related technologies (XML, JNDI, RMI, JMS, MDB) required
  • Hands-on coding experience with: Java, JSP, STRUTS, HTML, EJB 2.0 (Session and Entity), JDBC, JAXP, JMS, and JTA required
  • Experience with container managed persistence, container managed relationships and EJB query language
  • Prior experience with Object-oriented analysis and design, including use case development, class modeling, J2EE design patterns (Factory, Façade, Singleton, Business Delegate, Service Locator, Dependency Injection, Data Access Object) and UML required
  • Hands-on knowledge of application security support for e-commerce applications and relational database programming using SQL and JDBC required
  • Ability to write and modify large ANT build scripts supporting builds and deploys in multiple environments
  • Applied knowledge of JBoss and Configuration Management Processes is highly desirable
  • Must have the ability to work in a team environment and work independently on client engagements
  • Experience in professional consulting or like industry required
  • Strong written & verbal communication skills required
  • Keen interest in emerging Java technologies and industry best practices required
  • Ability to set technical direction for application architecture and development discipline, and assess the impact of architecture changes on the business and application is required
  • SCJP, SCJD and/or BEA Developer Certification is a plus
  • Experience with SQL, JBoss, Hibernate and Spring strongly preferred
Education:
  • BS in Computer Science, Engineering, or other related technical discipline required

Wednesday, November 01, 2006

Next Gig: Gateway JUG, St. Louis, Nov 7

Well the arrangements have all been made for my trip down to St. Louis for the November Gateway JUG meeting. I'll be presenting the usual fare, JRuby and JRuby on Rails stuff, except this time there's a few twists.

Twist One: I'll be presenting for 2+ hours

Yes, this one has me just a bit nervous...not because I think I'll have a problem presenting for that long or because I don't think I'll have enough material, but because I want to keep people awake the whole time. I've been told I'm a pretty good presenter, but even the best presentations can drag on after a while. Which leads me to the second twist:

Twist Two: I'll do 80-90% of the presentation manually

Yes, that's right...I'm going to be using IRB, command-line tools, IDEs, browsers, etc for most of the presentation. I think I've got my typing speed up to a comfortable enough level to keep people interested, and it's quite a bit more fun to watch things happen live than to see pictures or read slides. Plus it will be a unique challenge to do what basically amounts to a two-hour demo. You know me and challenges.

So a few links to information:

Gateway JUG - The Gateway JUG home page
JRuby: Bringing Ruby to the Java Platform - Description of the talk, my bio-of-the-month, directions and other info

And a rough overview of the talk (yes, this is the short, short list. I've got two pages for the long list):

  • Intro to Ruby and JRuby
  • Interactive demo of Ruby's major features
  • What JRuby adds
  • Interactive demo of JRuby
  • Break
  • Intro to Ruby on Rails
  • What JRuby adds
  • Building a simple Rails app
  • JRuby status and future
  • Conclusion
If you're going to be in the area on November 7, come watch me battle demo gremlins for two solid hours. It should be rife with action and adventure, love and laughs, guts and glory. Or at least you'll get to see both a JRuby and a JRuby on Rails presentation in one fun-filled night for free. That's worth it, no?

Update:
There's an event flyer now as well.

Compiler Progress: MultiStub and Full-Script Compilation

I spent today hacking on the Ruby to Java compiler and made some good progress. Here's the highlights:

  • It now parses a full script rather than just single method bodies.
  • The toplevel of the script is given its own method, and defined methods get theirs.
  • It uses MultiStub to implement the methods, so it will be faster than reflection.
  • It's more aware of incoming arguments, rather than assuming a single argument as in the previous revision.
  • It's generating a bit faster code, maybe 5-10% improvement.
MultiStub is our way of implementing many relocatable methods without generating a class-per-method or using reflection. We're using it today in our Enumerable implementation, and it works very well. Some numbers comparing our two reflection-based method binding techniques with MultiStub:

Invocation of a noop "test" method:
t = Time.now; 10000000.times { test }; puts Time.now - t
# test is alternately implemented using each of the three techniques
Control run (with no call to test in the block):
4.33s
ReflectionCallback-based (like Kernel methods today):
20.7s
ReflectedMethod-based (like most methods in normal classes):
19.3s
MultiStub-based (like Enumerable today):
14.9s
So simply switching to the MultiStub trims off around 20% for this benchmark. Removing the time to actually do 10M invocations of the block it comes closer to the 30% range. We're looking to start using MultiStub more in core classes. Anyway, back on topic...

What still needs to be done on the compiler:
  • I didn't implement any additional nodes, so it only handles perhaps 20% of them.
  • The toplevel method should define the contained methods as they're encountered. I'm wiring them all up manually in the test script right now.
  • It doesn't have any smarts for binding Ruby method names to the generated MultiStub methods yet.
It's a big leap in the right direction though, since you can pass it a script and it will try to compile the whole thing to Java code. Here's the results of the recursive fib benchmark with the new compiler (calculating fib(30)):
Time for bi-recursive, interpreted: 14.859
Time for bi-recursive, compiled: 9.825
Ruby 1.8.5:
Time for bi-recursive, interpreted: 1.677
This was in the mid 10-second range previously, so this is the first time we've dropped below 10 seconds. This puts the compiled code around 6x as slow as Ruby for this benchmark, which is very method-call intensive. Still, it's a solid 33% improvement over the interpreted version...probably an even larger percentage improvement if we don't count method-call overhead. Now on to iterative results, which are very light on interpretation (calculating fib(500000)):
Time for iterative, interpreted: 58.681
Time for iterative, compiled: 58.345
JRuby sans ObjectSpace support:
Time for iterative, interpreted: 47.638
Time for iterative, compiled: 47.563
Ruby 1.8.5:
Time for iterative, interpreted: 50.770461
For the iterative benchmark we're still about on par with (or around 20% slower than) Ruby because there's no interpretation involved and Java's BigInteger is faster than Ruby's Bignum. When ObjectSpace is turned off (it's pure overhead for us), the iterative version runs faster in JRuby. Once we eliminate some method overhead, things should improve more.

Moving right along.

Monday, October 30, 2006

Transcript of Second Life Presentation

There's now a complete transcript of my JRuby presentation for the Rubyists of Second Life complete with my slides and all the inter-avatar discussion. It's raw and it's real, and it was a lot of fun.

Larry the Liquid has also posted a JRuby of Second Life Recap that gives readers a good idea what it was like to "be there" and has a few photos to show it off. I'm the one standing down by the screen, and though you can't really tell, I'm wearing a JRuby t-shirt.

If only it were as cheap and easy to print t-shirts in Real Life as it is in Second Life...