See, here's the deal. I'm not a very advanced IDE user. I used Eclipse for years and JDE before that, and I wouldn't ever have considered myself a "pro" with either. I learned what I needed to get the job done and not a whole lot else. Beyond that I never knew some of the coolest features, and was never so good at convincing people to make a switch. This actually has helped my migration to NetBeans, since it basically does all the things I was used to in Eclipse. But it has been problematic when I try to convey how cool the new NetBeans Ruby support really is.
It's super cool. It does things I've not seen any other IDE or editor do.
However, it's best not to take my advice, and Roumen Strobl has recorded two seriously excellent demonstrations of Rails and Ruby support in NetBeans. They make the sale way better than I could (though I now have many good ideas for how to sell it better in the future).
If you have any interest in Ruby editors or IDEs, you should take the time and watch these, especially the Ruby support.
Two Demos: JRuby on Rails and Advanced Ruby Editing in NetBeans!
Tuesday, March 06, 2007
NetBeans 6 Ruby Support Even Better Than Expected
Behind The Scenes: JRuby 0.9.8 Released!
Things have been moving quickly in JRuby-land, and we've just kicked out a major release in JRuby 0.9.8. Among the big features:
- Ruby classes can extend concrete/abstract Java classes and override methods
- New Java primitive array syntax
- Reimplementation of String, Numeric classes, and Array to be more correct and performant
- Significant bottlenecks have been identified. In some cases IO is 6.5x faster than previous releases. Java included classes are significantly faster than in the past.
- 220 Jira issues resolved since last release
- Ruby on Rails support
It was our original plan over two months ago that by Februrary's end we'd be passing enough of Rails' own test cases to call it "supported". And in a relative rarity for an open source project, we hit that milestone. It was actually early last week we started saying we officially support Rails, which at the time meant better than 95% of Rails test cases passing. By the time we cleaned up the remaining release items, we had passed 98%. And we're still climbing.
So what does this mean for a practical Rails user? Well, it means that the dream of deploying Rails apps on any Java-based server in any Java-based organization is another big step closer to coming true. I've actually been demoing Rails apps running in a WAR file on GlassFish for all my recent talks, and it's pretty solid. I don't imagine it's perfect, of course, but it's looking really very good. It will be "ready" by the time we hit 1.0.
A few folks have been wondering about the jump from JRuby 0.9.2 to JRuby 0.9.8. The simple answer is this: we're two releases away from 1.0. We wanted to make it obvious how much work has gone into 0.9.8 (as many SVN revisions in this release as the last three combined) and show our commitment to getting a 1.0 release out very soon. The 0.9.8 release will be followed by 0.9.9, which will be followed by...well, you get the picture. We're in the home stretch now.
The other core team members have reported on the release, so I won't cover all that. Here's a list of links:
Tom Enebo
Ola Bini
Nick Sieger
In related news, there's also a new release of ActiveRecord-JDBC, the gem-installable module enabling JDBC databases to be used by ActiveRecord. This is also very welcome, since there have been a number of bug fixes in trunk that weren't easily installable the past couple months. Now you should be able to run both Rails 1.2.2 and 1.1.6 well with JRuby, just using released code. It helps complete the puzzle.
So what's next for JRuby? Well, with the 0.9.8 release we've reached a really big milestone as far as Ruby compatibility, so we're likely to turn our attentions toward areas not directly related to Ruby 1.8. Specifically:
- Work will continue on the bytecode compiler, and I'm hoping to have the JIT permanently enabled within the next week
- Java integration will get a heavier focus, to start bringing us more in line with languages like Groovy for ease-of-integration. In general things are looking pretty solid right now, but we know there's a lot of work to do here. I'd like for us to come as close as possible to Groovy's level of tight integration as possible.
- We will also start working on native Unicode support, by exposing a Chars class like the one provided in Rails' MultiByte library. This will allow us to maintain compatibility with Ruby and provide solid Unicode support without introducing an incompatible set of features.
- Both Tom and Ola have expressed an interest in continuing work on our Ruby 2.0 bytecode engine. Ola would like to continue improving and expanding the bytecode support, while Tom would like to play with a second parser that goes straight to bytecode. Both of those will help feed into a potentially faster interpreted mode in the future, giving us many of the perf gains shown in Ruby 1.9 benchmarks.
- JRuby interpreted mode is roughly 3-4x as slow as Ruby 1.8
- JRuby compiled mode is roughly 1.5-2x faster than Ruby 1.8
- KRI (Ruby 1.9, "Koichi's Ruby Implementation") is many times faster than MRI for the posted benchmarks
- KRI is 2x or less faster than MRI for the general case
As always, we welcome contributors, bug reports, patches, and user anecdotes for JRuby. We love to hear about bugs, we want to know your opinion, and we respect and honor our community whenever we get the chance. Remember, JRuby is your project, and can only be successful with your help.
Visit www.jruby.org to learn more about the project, download the release, and get involved!
On to the next release!
Thursday, March 01, 2007
JRuby World Tour: Bangalore
After my adventures in Hyderabad, the next stop on the tour took me to the heart of India's IT industry, where I would present my TechDays talk again for Sun Microsystems' Indian Engineering Center. But I scheduled in a little free time as well, to do some exploring.
I arrived on Saturday the 24th after a short flight on Sahara Air. For once, my travel arrangements worked out, and I was able to buy my ticket upon arriving at the Hyderabad airport. A few hours later, I had landed in Bangalore. A hotel car was waiting for me (this is now only the second time I've had a car waiting with my name on a sign, and I think I like it), so I headed straightaway to the Richmond Hotel in central Bangalore.
The Richmond has a fairly old-world feel, apparently having been converted from an apartment or office building some time in the past. It had a musty smell, but was otherwise pretty clean and well-kept. I'm generally more comfortable in "clean and comfortable" hotels than in "pristine" hotels like the Novotel, so the Richmond was just fine. That night I made arrangements for the next day's tour, ordered in some room service, and caught up on sleep.
It still amazes me how everything has changed since blogging became mainstream. Even as short as five years ago, I had a devil of a time finding local tour guides for my travels. Generally the best bet was to fork over too much money to a paid guide or to travel with a tour group. Neither of those are really conducive to free-form exploration, so I've always been forced to strike out on my own. But those days are certainly behind us.
In response to my "World Tour" blog posts, I had no less than 5 different people offer to show me around Bangalore. They ranged from JRuby enthusiasts to casual readers, and for once I had more guide offers than I could use. So I settled on the first two to contact me: K "Venkat" Venkatsubramaniyan and Håkan Raberg.
Venkat was up first on Sunday the 25th. He had actually stopped into the JRuby IRC channel (#jruby on FreeNode) as well as emailing me. His plan for the day included a trip to a cultural/folk center outside the city, lunch and dinner at a couple places to be determined, and shopping stops for a few things I wanted to purchase. So we started out bright and early at 9AM.
We made the following stops, in order:
- Rajarajeshwari Temple in western Bangalore, where I paid my respects and was gifted with a wreath of flowers and a tilak on my forehead. It was a moving experience, even though I felt a bit out of place in my western clothes. And the classical architecture of this modern shrine was quite impressive.
- Janapada Loka (Folk-Cultural World), a collection of folk art museums and exihibits on the road from Bangalore to Mysore.
- Kamath Yathrinivas Restaurant, where I had my first traditional Indian meal, complete with banana-leaf plate and eating with my hands. It was very tasty.
- Nalli Silk Arcade, to purchase sarees for my wife. I think I was the only guy in there shopping, other than a few poor saps who were obviously dragged along by their wives.
- Cauvery Crafts Emporium on Mahatma Ghandi Road, a nice collection of handicrafts, clothes, and nick-nacks, but very much tailored to western tourists. I looked at pearls for my wife briefly here, but decided not to buy.
- Navaratna Jeweleries, up the road from Cauvery, where I found the pearls I wanted. I went back on Tuesday before flying home to have the string extended and a clasp put on.
- Garuda Mall and Bangalore Central Mall...we just kinda ended up here looking for a place to buy tea and wasting time before our evening's restaurant opened.
- Amaravathi Restaurant, another more traditional place (more banana leaves and eating with hands), but this time with some delicious spicy curries and a crab dish as tasty as it was difficult to eat.
Monday the 26th was my day on the spot at Sun Microsystems. Conveniently, Sun's office in Bangalore was only about two blocks from the Richmond, so I headed over at around 10AM to prepare for my 11AM talk.
For some reason, every Sun office I visit is a terrific maze of doors, offices, and cubicals. The Menlo Park office has all L-shaped buildings with multiple intersecting hallways. Santa Clara's offices are plus-shaped (I think) which is often even more confusing. The Bangalore office put them to shame. It is hosted on the 4th, 5th, 6th, and 7th floors of a circular building with a central unsecured walkway and multiple secure entries and exits as you progress around the outer ring of offices. What complicated things most was that hallways extended out from the center of the ring like spokes of a wheel, but the perpendicular hallways did not always curve to match the circle. This meant that hallways often met at bizarre angles, and it was impossible to maintain any sense of direction. I got lost more than once wandering around, and unlike the California offices, I had no guarantee that following a given hallway would eventually lead me to an outside wall. Bizarre.
Anyway, I did the same talk from TechDays for Sun folks from 11AM to 12PM. Unfortunately, I had lost favor with the demo gods. My NetBeans demonstration froze up and refused to come back. My Rails demo failed with an error I still haven't isolated. And because of a peculiar scheduling snafu, the last 15 minutes of my talk had to be condensed into five minutes, resulting in more errors and a very flustered presentation. What had gone perfectly a few days earlier at TechDays went very poorly at Sun. Luckily, it seems to have still been well-received, and the classic JIRB demo was enough to wow the crowd.
The rest of the day was spent meeting with various folks at the IEC, talking about their projects and how JRuby could help. There's some cool stuff coming out of Sun India, so keep your eyes and ears open over the next few months.
Monday night my new friend Hakan Raberg from ThoughtWorks arranged to meet with me and two other ThoughtWorkers for dinner. It turns out Hakan is an old friend of Anders Bengsston, one of the premier JRuby contributors in the old days before I joined the project. Such a small world.
As a nice change of pace, the ThoughtWorkers and I had dinner at Barbeque Nation, a trendy, modern-feeling restaurant featuring fairly traditional northern Indian cuisine. In other words, lots of meat. Barbeque Nation is one of those "cook it yourself" places, where the center of the table is a small charcoal stove and the meats are delivered mostly-cooked on metal skewers. After marinating with a choice of marinades and allowing the food to cook the last 10% of the way, a delicious entree results. To make it even more acceptable to a western eater: it's all-you-can-eat, with skewers continually delivered until you tell them to stop. For the first time in a week, I was stuffed. It's very hard to turn down delicious seasoned and grilled meats when they keep arriving in front of you.
The ThoughtWorkers also had a number of interesting projects going on, many related to Ruby and a couple likely to use JRuby in the near future. Hakan had even worked on a code-generated implementation of the JRuby selector table's switch statements, which he promised to tidy up and send me. It could be very useful for us to optimize user-created classes in the same way we're starting to optimize core classes, and it was actually a technique we considered doing ourselves. More on that as it develops.
Tuesday, I packed up and checked out of the hotel. I was a little sad to be leaving Bangalore, but since I still had an entire day before my flight I walked up to M.G. Road to take care of some last minute souvenir shopping. My first stop was at Navaratna Jewelers for the modifications to my wife's new pearl necklace I mentioned earlier. While I waited for the work to be completed, I visited a few shops off the main boulevard and purchased the following items to bring home:
- A small wooden Ganesha (about 4" tall), to bring success and prosperity to my office.
- A small wooden Emblem of India, atop a 4" wooden column with a felt base. It is a perfect reminder of my Indian adventure (and probably the coolest national emblem I know). This is my favorite souvenir by far.
- A small wooden puzzle box for my son.
- Two larger versions of the puzzle-box with hand-painted images of Indian women on the tops.
- Two silk ties with different images of dancing elephants on them.
- Two painted clay hippopotamuses, as gifts for whoever.
- A comic/cartoon book of stories of "Heroes of the Mahabharata", for my boy to read.
Comfortably full from lunch and loaded down with all my new possessions, I returned to the hotel to catch a cab to the airport. Goodbye, Bangalore...or so I thought.
Bangalore Airport was to be a baffling ordeal. People were lined up for a solid block to get into the terminal, mostly encumbered with a half-dozen bags each. To confuse even more, there was an option whereby non-flying visitors could pay for admission to the terminal. Luckily, I figured out I didn't have to pay, and after muscling my way to the entrance, I moved on to the second challenge in this travel gauntlet: Thai Airways had no reservation for me.
My original tickets to India were incorrectly issued to "Charles Onutter", an unfortunate combination of my middle initial and last name. Normally, this wouldn't have been a big deal, but apparently international flights are extremely strict about the name for which tickets are issued. If it doesn't match your passport exactly, you're out of luck and can't check in. In a flurry of activity the day before I left, I managed to arrange new tickets. But because it was such short notice, I would have to buy two of my tickets at the originating airports: Sahara Air from Hyderabad to Bangalore and Thai Airways from Bangalore to Bangkok. Sahara air went fine...either because they found my reservation or because they had plenty of room on the flight. But Thai Airways had cancelled my reservation because they overbooked the flight.
Luckily, after talking to the ticket agents and American Express's emergency travel service, I was able to purchase an unused seat for the flight. I would fly first-class to Bangkok. I rushed through security, ran to the gate, and finally settled into my seat. Onward to the next stop in the tour: Bangkok, Thailand!
Friday, February 23, 2007
The Great Hyderabad Adventure
I have many pictures to share, but they will have to wait for an updated post. Allow your imagination to escort you for now.
So! I had been trapped in the "green zone" at the Novotel Hyderabad for two solid days, and I think I just finally had enough of it. The Novotel is surrounded by about five acres of land, and the edge of that property terminates with a heavy fence (or wall in places) with either barbed wire or razor wire keeping the rabble out...or perhaps keeping the westerners in? I had taken a few strolls on the grounds, and after walking a few hundred meters from one end to the other this morning, I realized I was tired of living in the bubble. It was time to leave.
A number of folks had expressed interest in escorting me into and around Hyderabad, but nothing materialized. Since this was my last chance to see the city, and potentially the last time I'd be in Hyderabad for many years, I had to take matters into my own hands. I asked the front desk for a map, a guidebook, and a few recommendations. Then the transportation desk set up a cab ride to my first destination, Charminar, the square, four-towered heart of the city. It was a perfect if clichéd place to start my day of exploration.
Unfortunately, Charminar is also right in the heart of the "old city", the other half of Hyderabad housing the less privileged portion of the population. Charminar itself was certainly astounding. Even though the minarets were closed, the view from the top main floor was quite impressive. In each of the four directions, thoroughfares stretched off into the distance, revealing a bustling mass of humanity. I could see the swarming rickshaws, the teeming bazaar, and the hoards of Hyderabadi citizens going about their lives. It was humbling really. But it was a bit more personal once I descended back into those masses to find my next destination.
India has truly awakened. Here more than anywhere I have visited the contrast between technology and tradition is grossly apparent. Construction projects involve teams of mean working by hand, like ants assembling a colony. But every ant has a cell phone with text messaging and a camera. In the streets, beggars clutch at you constantly, but often speak extremely capable English and understand the western world. Above, signs advertise BizTalk Server and Oracle Training. Below, people sleep in the streets and beg for pennies in roaring traffic. And in the streets of Hyderabad this paradoxical turmoil boils unabated.
So I climbed back down the stairs of Charminar, a bit concerned for my future. Walking out the secured gate, I was immediately mobbed by beggars, young and old. A few (usually the youngest) spoke surprisingly good English. They asked where I was from, what I needed, where I was going. Some (the older members of the hoard) only knew what they needed to elicit a response from western hearts and minds..."food"..."baby"..."hungry"..."please". Why exactly had I decided to launch into this city alone? Ahh, for adventure, I remember. And what happens at this point in an adventure? When the odds and numbers are stacked against you? A gallant escape, with nary a rupee spared. How noble of me.
No, I did not make a contribution to the Hyderabadi poor. I certainly could have. Is it heartless of me? I don't think so; to have made one contribution would have certainly brought a torrent upon me, and it was already a worrisome situation. Instead, I made for a nest of rickshaws near the monument. One particularly bold youth, Abdul, stepped out of the crowd with a map of monuments, a plan for visiting them, and a welcoming smile. He was a salesman with exactly the product I needed, and for the next six hours he would be my guide. Off we went.
One of the previous would-be escorts had suggested Golkonda Fort as a place I should not miss. So naturally, with a friendly and aware guide at my side, that was the next place to visit.
Golkonda is the ruined former home of the Qutub Shahis, before they moved their home to what is now Hyderabad. It is an extensive maze of towers, walls, tunnels, temples, mosques, and overlooks atop a the highest hill in the area. After climbing several hundred feet to the summit, the sprawl of the city extends in all directions. No other view in Hyderabad gives such a complete picture of its size and extent. The fort itself is also impressive, with four-story defensive walls, a remarkable acoustic communication system, and numerous gardens tucked away behind tunnels and through towering arches. The climb to the top required a few breaks, during which Abdul told me what he knew of the fort's history in broken English and snapped photos of me at the major landmarks.
Eventually, we descended, and headed for the next stop on the tour: the Qutub Shahi Tombs.
There were seven Qutub Shahi kings, and they are all interned here in monumental tombs. Each tomb hosts an enormous dome (complete with perfect acoustics for singing, chanting, or tourists clapping and hooting), multiple entrances and side-tunnels, and inside the center, a symbolic (or perhaps more-than-symbolic?) monument to the hosted monarch. Most of the tombs contain a single such monument, but one had three: mother, father, and child. There are tombs here for the seven kings, many of their wives, and other relations. The writing on the monuments is in Arabic. That on the tour signs, in Telugu. Both Hindi and Muslim visit and pay their respects. And the city surrounds. I would say that the Qutub Shahi Tombs are at least as important a stop as the Golkonda Fort. If you are to visit the height of the kings' success, you should at least say an appropriate goodbye.
The rest of my adventure is somewhat less compelling, but perhaps still interesting. Hyderabad as we know it in the western world is really two cities to the Indians: Hyderabad and Secunderabad. In a peculiar quirk of fate, this pair is known to most Indians as "The Twin Cities", just like my home towns of Minneapolis and St Paul are known to Minnesotans (and many Americans) as "The Twin Cities". The very center of Hyderabad, bridging these two cities, is a large lake called Hussein Sagal. Arriving here, I have come full-circle to the heart of the "Twin Cities" on the other side of the world. Unfortunately, the interesting part of that story ends here. Hussein Sagal now appears to be little more than a tourist trap, firmly ensconced in the "new" part of the city. It is surrounded by miraculous science museums and modern family amusement parks. Put simply, I had no interest in anything more than driving past. Yes, it's a large lake in the middle of the city. Yes, there's a 60-foot-tall Buddha standing in the center. The noise of the traffic surrounding and the lights of the shopping malls and Pepsi signs tends to dampen any beauty or majesty that once was there. We moved on.
One item I had expressed interest in when talking to the front desk was "shopping". Unfortunately, we had different definitions of shopping. To me, shopping was visiting a "bazaar of the people", where I could browse sarees for my wife, hand-made toys for my son, and myriad other handicraft and localities. To the hotel, shopping meant "Macy's".
360s, $1 The final stop on my 8-hour Hyderabad tour was, sadly, the LifeStyle department store. It wasn't a bad store, really, since it did host a few local items and the prices were a little better than those at home. But it was a truly western-style store, with little interest to me as anything other than a landing zone. I wandered up to its second story, where I found discounted XBoxVCDs, and a coffee shop so grossly plastered with Windows Vista branding that they even had a "Windows Logo" menu prouding selling "Windows Vista WOWffee". Naturally, I ordered one due to the sheer absurdity of getting a free "Vista Wristlet", a white rubber bracelet emblazoned with the words "Windows Vista. The "Wow" starts here." To make it even more stomach-turning, it was a latte with orange flavoring. Delicious.
Of course I couldn't let the day end with a "WOWffee" and high-quality merchandise at low, low prices, so I called a fellow Sunny from the hotel to join me for a very satisfactory dinner. And then, after consuming excessive amounts of vegetable biriyani, chicken tikka masala, and garlic naan, I returned to the "green zone".
So what have I learned from my excursion?
- Exploring the poor districts of an unknown city alone can be frightening, but it certainly gives one perspective on the meaningless nonsense we labor over in the western world.
- Don't wait for someone to show you the world; just go see it yourself.
- The world is disappearing...so you better see it soon.
Wednesday, February 21, 2007
Tech Days Talk a Success
I just completed my talk at Tech Days Hyderabad, and I think it was resounding success. If I heard right, the total head count for my "JRuby Essentials" talk was over 1200 people, by far the largest crowd I've presented for. There was a hoard of questioners immediately after the talk, who followed me into the hall to keep asking. I gave out my entire in-pocket batch of business cards, and even received a round of applause after the JRuby on Rails demo. All this even after my talk was delayed and cut to 40 minutes because the earlier talks ran too long. It was a sprint, but I think it went very well.
I will be posting the slides, but they're basically the same content many of you have probably seen the past couple months. For those who haven't had the pleasure, we'll start posting slides more regularly, and I'll try to blog a few example walkthroughs for those of you who want to duplicate them for your own talks. Feel free to steal it all and spread the word.
Tuesday, February 20, 2007
JRuby World Tour: Hyderabad
Success! It is now 3:00AM in Hyderabad India, and I have checked into the Novotel in HiTec City. The flight was uneventful; I got my demos basically working and then slept the majority of the time. The immigration and customs at the airport were perhaps the least threatening of any I've seen. Immigrations basically just looked for my visa, took my arrival card, and stamped my passport. Customs was little more than a big white X-ray machine with "CUSTOMS" printed on a white piece of paper taped to the front.
The smooth arrival was just what the doctor ordered. The reason I'm on this round-the-globe trip is because my original tickets to India were issued to the wrong name: Charles O'Nutter instead of Charles (O) Nutter. I never noticed the slip-up on the itinerary (nor would I ever have recognized it as a slip-up, since the output was pretty mainframe-ish), and only upon arriving at the airport in Minneapolis was I told I could not check in. Twenty-four hours and a lot of scrambling later, I had new tickets issued on the only flights available, hence my return trip heading east via Bangkok and Tokyo. But aside from that initial snafu, the trip has been pretty routine. Saturday will be interesting...my ticket to Bangalore has to be purchased in-person, so there's great potential for more headaches. But I can take it in stride, and I'm sure things will work out well in the end.
Hyderabad is about what I expected, and I'm looking forward to getting into the city one of these three days before I leave for Bangalore. It feels very similar to Beijing, with old meeting new and construction everywhere. The city looks very appealing; street-level shops line almost every sidewalk, there's green plant life everywhere, and everything has sort of a dusty, dingy, comfortable feel. It's not dirty, don't get me wrong...just dusty, like any city kicking earth into the air with a thousand construction projects might be. The auto-rickshaws (three-wheeled rickshaw-looking things with small engines) are pervasive; I can imagine that during the day they swarm and buzz like bees. They drive on the British side of the road, which isn't much of a surprise, really.
It's really unfortunate when traveling to more exotic places like this that I have to check into a tidy, pristine western-style hotel. I'd rather stay in the midst of the city, where I can walk out the front door into daily Hyderabadian life. I doubt I'll be at the hotel for all three days of the conference...there's exploring to be done.
If you're located in Hyderabad or Bangalore and might be able to show me around a day this week (Thursday or Friday for Hyderabad) or next Sunday or Tuesday (Bangalore) I'd appreciate it. I'm not put off by crowds of people, even if many don't speak my language, but knowing the hot spots for shopping and eating makes exploring a bit more productive.
I give my presentation in just over 12 hours, so I'm going to try to settle in with a nice Kingfisher lager beer and relax. The conference schedule has things starting up in about 6 hours, so I should be fine for sleep and fairly well-delagged by then.
On the plane, I managed to duplicate the rails-integration WARfile-based deployment of a simple webapp, but I was unable to get an embedded Derby DB to work with it. I'm sure it's a matter of permissions, connection pooling, and such. I may or may not have time to get that working by tomorrow, but if I demo the basic app working with WEBrick and then WAR it up and show the non-DB stuff working in GlassFish, I think the point will have been made. I must also keep reminding myself that my talk is only 50 minutes now. I can fill 50 minutes without batting an eye. So we'll see if there's even time to get to GlassFish, since it eats up a good five minutes building and deploying the app from scratch.
JRuby World Tour: Amsterdam
I'm now relaxing at Schiphol Airport, where I've just plunked down my 10 euros for internet access. After a light breakfast, my current task is now to ensure demos are ready and working for TechDays. I discovered a bit ago that the Rails Integration project uses maven to build, and so I need internet access for that first install command (to pull down required dependencies). I had worried a bit about finding internet access, but this connection appears to be pretty good. I've managed to build the Rails Integration module, and hopefully I'll be able to get Rails-in-a-WARfile working in the next few hours.
If I discover anything interesting in the process, I'll post it. From what I've observed, the rails-integration guys and Ashish have had lots of success lately.
If you happen to be at Schiphol before I depart at 11:55, I'm in Lounge 2 near the brasserie. I'm the one with the MacBook Pro, fighting jetlag and overlooking the concourse.
The next stop will be Hyderabad. My 11:55 flight departs from gate F8.
Update: I managed to get Rails up and running nicely in a WAR file. Huzzah! I'll play with it a bit on the plane and get our usual Rails demo to feed directly into "now we'll make it a WAR file". Thanks much to the rails-integration guys for putting together a really slick piece of work. Gotta love this whole open-source thing.
Side note: jet lag is a weird feeling. Right now it's 9:49 in Amsterdam and around 14:19 in Hyderabad, so I have to pretend it's mid-afternoon. My flight will board in about two hours, and toward the end of the flight I'll have to pretend it's evening and start getting a little sleep. It's quite unfortunate that I'm arriving in Hyderabad at 1:20AM on the day I'm presenting, but that's how it goes!
Sunday, February 18, 2007
The JRuby WORLD TOUR 2007
Yes, you read that right! I'm announcing my intent to circumnavigate the globe in only NINE DAYS spreading JRuby and joy at every stop. The Earth shall be wrapped in JRuby goodness!
To celebrate this monumental occasion, I'm inviting all developers with an interest in Ruby or Java to join the JRuby mailing lists and volunteer your services. Eternal gratitude and temporary fame could be yours, for the small price of bug reports, bug fixes, or high-performance rewrites of core JRuby libraries (it's so easy!). Operators are standing by to receive your emails and direct you to the promised land!
Now, on to the tour!
First Stop: Amsterdam, Netherlands
The first leg of my journey takes me from my home in Minneapolis, Minnesota to beautiful Amsterdam. There I will enjoy an delicious airport breakfast followed by a five-hour tour of the terminal. I will be available for autograph signing from 7:00 to 11:30 by appointment only. The first annual JRuby New-Age Concert of Magic will follow from 11:30 to 11:35. Tickets are first-come, first-served; cash only, please! I will depart from Amsterdam at 11:55 via a chartered jet I'm generously sharing with three-hundred other passengers and a small KLM Dutch Airways flight crew, en route to exotic Hyderabad, India!
Second Stop: Hyderabad, India
The next part of the JRuby "World Domination Tour 2007" takes me to Sun's Tech Days event in Hyderabad. In addition to my JRuby session, Tech Days will host such scintillating topics as "iPod Giveaway" and "Java Jacket Giveaway". But on Wednesday the 21st at 3:35PM you too can learn why JRuby is the programming alchemist's "Developer's Stone", transmuting static lead into dynamic gold. The topics covered will be exactly like those in recent JRuby talks, except cooler, faster, and after 26 hours of non-stop travel and five hours of sleep. Prepare for a punchy, laugh-riotous affair! Will I be able to maintain 90WPM during my interactive demonstrations? Will I remember that "alias" takes two arguments *without* a comma and with the original method first? Join me for what's sure to be 50 minutes you'll remember the rest of your life!
Third Stop: Bangalore, India
From Hyderabad, I take a short one-hour flight to Bangalore, home to Sun Microsystems India and the third leg of the JRuby Globe-trotting Festival of Light! On Monday, February 23rd, I will present JRuby to my tropical counterparts on the opposite side of the earth, featuring the exact same topics from Tech Days...in RANDOM ORDER. You never know what I'm going to do next.
I actually have all that Sunday free and Tuesday up until about 8:00 free to do some exploring. Event suggestions are welcome, and I challenge any of you to find food too spicy for me to eat! (impossible, I say!)
Fourth Stop: Bangkok, Thailand
The next step takes me from Bangalore to beautiful Thailand, jewel of Southeast Asia and home to one of my favorite cuisines. I will be available from 4:00AM to 5:30AM for a special, once-of-a-lifetime event I'm calling "JRuby Dawn at Bangkok Airport". And the great question on everyone's lips will be foremost on my mind:
Will the airport's Thai restaurants open before I board my next flight at 6:00? Stay Tuned!
Fifth Stop: Tokyo, Japan
Continuing the Asian leg of the tour, I'll spend 80 fun-filled minutes exploring the international terminal in Narita, only an hour's train ride from the Emperor's Palace! Naturally I'll be available for handshakes and baby-kissing, and hopefully the always-humorous photograph of "buying beer from a vending machine". This trip will serve as a preview for the main Tokyo event: Ruby Kaigi 2007 in June, where I'll finally present JRuby to the Land of the Rising Sun. Come 3:10PM it's time for "so long Japan", but I'll be back soon!
Sixth Stop: Minneapolis, Minnesota
And just 9 days after I departed, I'll be home in Minneapolis again, ready for my next thrilling adventure: The Greater Wisconsin Software Symposium (a No Fluff Just Stuff event) in Milwaukee from March 2-4. Join me for my two fabulous sessions "Bringing Ruby and Rails to the JVM" and "Become Super-Powerful with JRuby", putting the greatest dynamic language ever in the palm of your JVM.
Wednesday, February 14, 2007
Jython 2.2 Beta 1 Released!
The Phoenix Is Rising!
After being considered dead for many years, Jython is back in business with the beta 1 release of Jython 2.2. It's been teetering on the edge of 2.2 compatibility for a long time, but over the past several months the core team and several contributors have rounded off the edges, to the point that a beta release of the long-awaited 2.2 version is now available.
For all you Pythonistas, this should mean two things:
- You have a project for the day. Go get Jython 2.2, try it out with your Python 2.2-compatible apps and libraries, and report any issues you find.
- Start contributing your time, either helping with the Java coding, helping to debug Python apps and libraries, or help on efforts to write C-based libraries in Python.
I will also remind you that a large percentage of JRuby's success is due entirely to its community. Can you really get up in the morning and look at yourself in the mirror without knowing you've helped Jython get back up to speed? Can you?
Tuesday, February 13, 2007
Rails Support Status Update
Things are moving along well...so well I've found time to reimplement String with byte[], work on closures in the compiler, and, well those are posts for another day. Today I update you on the progress of supporting Rails in JRuby.
Rails is quite an interesting beast. I've learned more about Ruby looking through Rails code than from any other source...out of sheer necessity. They say the best way to learn a language is through immersion, right? Is debugging 50-deep stack traces on a questionable interpreter, digging for a reducible test case immersive enough for you? Yeah, I thought so.
We actually hit Rails support hard right at the beginning of this month, or the end of January. The early results were pretty solid, so our efforts have been distracted onto other large JRuby issues not necessarily Rails-related (but all still critical for an eventual 1.0). We also made the bold move of jumping to Rails 1.2.x for all our testing, to show we can keep up with the Rails development process. And to show that we've made great progress, I give you the following results.
ActiveSupport
ActiveSupport is the base module for Rails. It monkey-patches a number of core classes, provides a multibyte String wrapper for UTF-8 encoded text, and handles most of the details of running and configuring an application. It "supports" the other libraries, and is the first crucial leg needed to run Rails. And so we must "support" it well.
Here's the current results of a full test run:
498 tests, 1809 assertions, 16 failures, 6 errors
That's already in the 95% range, so we're in darn good shape. But it turns out a number of these errors are caused by a glitch in our parser related to KCODE. So if we work around that known issue, the results improve to:
498 tests, 1845 assertions, 12 failures, 1 errors
So more like 97% passing once we fix the KCODE parser problem. The remaining issues are almost all related to time-formatting bugs, with a couple multibyte and exception-handling issues tossed in. I haven't attacked the formatting bugs because Printf code is bloody painful, the multibyte issues are waiting on the KCODE parser fix, and the others...well, they're boring.
ActionPack
ActionPack is the brains of a Rails app, housing the mechanisms for controllers, views, and any code to support them. So it's leg two of the crucial three-legged support necessary to run a Rails application.
I hit ActionPack hard in the past, and a bit this month. Ola took off with it and completed most of the remaining failures. Running with the same KCODE workaround, we have the following results today:
1157 tests, 4811 assertions, 7 failures, 14 errors
That's above 98% passing. The remaining failures include a number of dupes (a single failure that breaks a number of tests), some additional string-formatting failures, and a couple that run ok outside of Rake. So it's damn close to perfect.
ActiveRecord
You should all know ActiveRecord by now, right? It's Rails' DB layer, based on the ActiveRecord pattern. It is the third core leg of the Rails platform, though you can certainly have apps that don't use AR for database support.
ActiveRecord is an extensive piece of code and it's the only part of Rails that usually requires a native library to run properly (though there is a pure Ruby impl of its MySQL support that nobody uses). In order to support AR, a number of the JRuby community members have cooperated over the past 9 months to build ActiveRecord-JDBC, a gem-installable module that provides ActiveRecord DB support via JDBC. It's a great bit of hackery, and runs surprisingly well considering our less-than-beautiful Java integration performance (under repair).
To limit the scope of this month's Rails work, we're targetting MySQL, since it's the de-facto standard for Rails apps in most quarters. But most of the work we're doing will apply equally well to the other databases, since JDBC is generally very consistent. Tom has been spending lots of time on Derby, for example, to the point that our ActiveRecord-on-Derby failures are almost entirely limited to SQL features it doesn't support yet.
So then ActiveRecord test results, minus the KCODE workaround (since Tom ran these for me):
1012 tests, 3417 assertions, 41 failures, 35 errors
Here our results dip to around 92% passing, but it's really the extended features of AR that have failures here. We've come a long way on this; results on the months-outdated JRuby wiki show Rails 1.1.6's ActiveRecord only passing about 60% of that release's tests, so there's been a ton of improvement since November. And we generally understand how to fix the remaining failures, so it's only a matter of (short) time.
ActionMailer
Can you guess what ActionMailer does? ActionMailer provides support for composing, formatting, and sending email from a Rails app. And that's about it. It's not a huge library, but it's essential for many apps.
64 tests, 142 assertions, 9 failures, 6 errors
That's about 75% passing, with a grand total of six test scripts. We haven't focused on this much, other than Tom's initial KCODE work a few months back. The current failures are almost all mail-formatting or SMTP-wrangling issues. I don't expect them to be hard to repair.
ActionWebservice
Because ActionWebservice's tests require some database setup, we haven't tackled them yet. But I would lay even money that they'll be comparable to ActiveRecord at their worst. For now, they all just fail because MySQL isn't set up correctly for them.
96 tests, 0 assertions, 0 failures, 96 errors
Any community member that wants to dive into ActionWebservice or ActionMailer would fast become a JRuby Hero.
Railties
Railties is the final piece of the Rails puzzle, and it...well..."ties Rails" together. I show the results here mostly for completeness; many of the libraries in Railties we'll never support (fcgi, for example) and most JRuby-on-Rails deployments will use alternative mechanisms for hitting the web.
5 tests, 19 assertions, 2 failures, 0 errors
Official Rails Support?
Because things are looking pretty solid, we've been looking for an answer to this question: What does Rails Support in JRuby mean? Do we have to pass all test cases 100% to "officially" support Rails? That might never happen, since there's POSIX and external library stuff we won't ever handle (nor will we need to for JRuby on Rails apps). So then is it a certain percentage? 95%? 98%?
I think the truth is that we could really announce support for Rails now. Almost all the visible, outstanding issues with actually *running* Rails apps have been resolved, and most apps and scripts work fine. There's ongoing work to improve ActiveRecord-JDBC's support for other databases, but that's an endless quest. And of course there's more work needed to support Grizzly, Mongrel, and WAR-based deployment of JRuby on Rails, but those are peripheral to the official announcement. Even when we do make an official announcement, it will be for a pre-1.0 version of JRuby, since we know there's another few months left on 1.0 fixes and features.
So what do you think, dear reader? At what point would you feel safe saying "Let's have our non-JRuby hackers try using JRuby on Rails"? You probably would *be* safe right now, since even if you found issues we've got a busy community ready to help solve them. And we'll probably tiptoe closer to "perfect" Rails support in JRuby over the next couple months, chasing the long tail of Ruby compatibility. But how do these numbers and this update make you feel about JRuby on Rails today?
And as always, we love to have additional contributors, so we'll bend over backwards to make it easy for you to help. Join the lists, join #jruby on freenode IRC, or toss us email privately. JRuby is an amazing community-driven success story, and the only thing missing is you.
California Schemin'
Last week Tim Bray and I were at Menlo Park to meet with the Open Source Software Society Shimane, a delegation of developers, managers, and company heads from the Shimane prefecture of Japan. They were visiting Sun to talk with us about opportunities for cooperation, Sun hardware and software, and most importantly: Ruby. For you see Shimane is the home of Yukihiro "Matz" Matsumoto, creator of Ruby, and he accompanied the group to California.
The evening before the event, we went to Fuki Sushi in Palo Alto, a short two blocks from my hotel. I don't believe I've ever eaten such quantity or variety of Japanese cuisine, and I think our guests felt the same way. They marveled at the size of most dishes, especially the ice-cream-scoop-sized lump of wasabi and the two-foot-long sushi tray. They also photographed almost everything...I think I posed for a couple dozen snaps.
During the following day, Thursday, they sat through numerous presentations on Sun hardware and software. Tim and I also discussed Sun's position on Ruby and JRuby for an hour before and about forty minutes after lunch. Tim hit the high-level points about where Ruby will likely fit into the Java ecosystem in the future, and I supplied details and demos of JRuby. I also threw in a demo of JRuby's compiler beating Ruby 1.8 in the standard fib algorithm, which elicited a smile and laugh from Matz himself (whew! I was worried how he'd react!).
Most interesting to me, however, was my discussion with Matz that night.
I was invited to join the delegation for a crab dinner in San Francisco. We went to Crustacean, a moderately upscale joint near California and 101. And after the attendant tied my plastic bib on, we were ready to go.
Since Matz and I ended up sitting together, and since very few others at the table spoke English, we managed to get in some time discussing a couple Ruby 2.0 design issues. Here's a quick summary:
- Matz seems to have come around to my visibility proposal for "private" in Ruby 2.0, which is largely the same as how Java handles private visibility. I believe this model is a good simplification over the original proposal. See ruby-core:9996 and related for the original discussion. The basic facts of private then would be:
- You must dispatch to private methods using a functional call, as in foo() versus xyz.foo(). I didn't like this at first, but I've come around to using call syntax to force certain aspects of visibility.
- Dispatches to private methods will only look in the same class for the method definition.
- Methods that are public in superclasses can't be made private in subclasses.
- Methods that are private in superclasses are not visible to subclasses, and so new methods of the same name and any visibility can exist in subclasses.
- Protected methods in Ruby 2.0 could potentially act like private methods now, though Matz is worried it would be too much of a change. I think it's appropriate; current private method behavior is very similar to Java's model for protected methods, where the methods can't be seen from outside the hierarchy, but can be called and overridden within the hierarchy as normal. I voiced my opinion, so we'll see where Matz goes from here.
- Matz is still comfortable with removing set_trace_func if a better mechanism for profiling and debugging can replace it. I had a few suggestions for alternate mechanisms, but I also promised to look into Java's model, since it seems to work quite well. I also suggested there may be something to learn from DTrace.
- Matz has come around to the idea that encoded character sequences are a different type than unencoded byte arrays, though he still wants them to have the same outward interface.
The topic of encoded character strings came up a few times during Matz's visit, usually with him asking how we're doing things in JRuby. I explained that we mostly just follow Ruby 1.8, with our String now being backed by a byte[], but that we're also providing out-of-the-box native support for the new Rails ActiveSupport::MultiByte Chars class, a wrapper around string that enforces character boundaries and encodings.
At dinner, we continued the discussion. I made my case for a separate type with the following points:
- A separate type would not require String's interface to change, and it could remain a byte array
- By having separate types, we can use polymorphic behavior to avoid checking and re-checking encodings for every operation
So the result of the String discussion can be summarized in a few points:
- String's interface will change from 1.8 to work with characters rather than bytes, both in the encoded and unencoded forms of String. The plan for String methods' behaviors does not change from current Ruby 1.9.
- String will have subtypes that represent encoded character data, though in most cases you won't need to know about those types. If you do need to go after a UTF8String (my name), you can, but there will also be some sort of factory model for generating encoded strings and Ruby 2's encoding pragma will handle literals.
Wednesday, January 31, 2007
The End is Nigh: Help Squash Rails Bugs
My friends, the end of the road is in sight for official Rails support.
Tom, Ola, and I have been working over the past week to get remaining Rails issues wrapped up. As a result of our efforts:
- ActionPack is now "practically" 100% working, minus a test or two we can't support and a few tests that are broken or that run fine in isolation (it would be nice to know *why* those fail)
- ActiveSupport is well above 95% passing
- ActiveRecord is in the 90% range passing with MySQL and in the 80% range with Derby
So here is a set of instructions on how YOU, dear reader, can help round out JRuby's support for Ruby on Rails.
- Getting a testbed set up
- Update JRuby trunk (http://svn.codehaus.org/jruby/trunk/jruby), build it (ant clean jar), love it
- Install Rake (gem install rake)
- Fetch Rails 1.2.1 from Rails SVN at http://dev.rubyonrails.org/svn/rails/tags/rel_1-2-1/
- Running the tests
- From within the module under test (like activesupport/ or actionmailer/) just run "rake". All tests should execute and a report should be provided at the end. You can usually run tests individually as well, although a few depend on the side-effects of previous tests.
- ActiveRecord requires some additional setup; I'll update this post with Tom's instructions and patches shortly.
- Reporting issues
- Reporting failures in Rails is good
- Reporting reduced test cases or clear explanations for failures in Rails is better
- Reporting reduced test cases and including patches for failures in Rails is BEST
- Don't forget to check if your issue has already been reported, and please sync up on the mailing list while you're working
- Patches will probably not be accepted without a reusable test case. We're trying to grow our regression suite as a result of this work.
- JRuby's JIRA is here: http://jira.codehaus.org/browse/JRUBY
- Caveats, things to watch for, things to try
- ActiveRecord (with the AR-JDBC adapter) could use wider DB testing. We've done quite a bit of work with MySQL and Tom has been improving Derby support, but there are lots of other databases out there. Pick your favorite database, follow Tom's instructions to get up and going, and report issues (in the JRuby JIRA at least, but also report to jruby-extras project if appropriate)
- Railties includes code that will never run under JRuby, like its fcgi-based dispatcher tests. You should confirm with us that they're expected, and then ignore or delete them for your future runs.
- Rails is a very...interesting...application to debug. Feel free to ask on-list if you simply don't get something. I've seen things in Rails code no man should have to see, so I know it can be frustrating to debug at times.
Update: A couple folks pointed out that the codebase didn't compile under Java 1.4.2. That has been corrected!
Update 2: A few folks are seeing a problem installing gems related to the %p operator to printf. We're working on that, and it's a fairly minor issue, but to avoid it there's one additional step before you install rake: set the JRUBY_HOME env var to the root of your JRuby stuff.
Tuesday, January 30, 2007
Improving Java Integration Performance
I created JRUBY-501 to track performance improvements to Java integration, since it's come to light recently that it may be one of our biggest bottlenecks now. And I found a ripe, juicy fix already.
For every call to a Java type, we call JavaUtilities.matching_method with a list of potential methods and the given argument list. matching_method compares the available methods and the types of the arguments, choosing the best option and returning it to be called. This is essentially our heuristic for choosing an overloaded method from many options, given a set of arguments.
Problem was, we didn't cache anything.
Given a list of argument types and a list of methods, there's only ever going to be one appropriate choice. Unfortunately our code was doing the search for every single call, and you can imagine how much additional overhead that added. Or perhaps you can't, and I'll show you.
Here's the numbers before my tiny change:
38.862000 0.000000 38.862000 ( 38.861000)This test basically just instantiates a StringBuffer and appends the same character to it 100_000 times. It takes roughly 40 seconds to do that with the old code.
40.230000 0.000000 40.230000 ( 40.230000)
And here's with my changes:
3.295000 0.000000 3.295000 ( 3.294000)Yes, you're reading that right. It's an 13 times improvement.
2.933000 0.000000 2.933000 ( 2.933000)
And the change was trivial: given the list of methods and argument types, cache the correct method. So simple, so elegant, so effective.
So does this affect regular Ruby code? You better believe it does!
I had been intrigued by the fact that some of the first methods JITed during rdoc generation were all JavaSupport methods. That told me something in rdoc was using a class we provide through Java integration, rather than natively or in pure Ruby. So I figured with this change, I'd re-run the numbers.
Before the change, a full rake install with rdoc took about 42s, or about 31s with ObjectSpace disabled. And now, the "after" numbers:
with ObjectSpace:This is by far the largest increase we've seen in rdoc performance in several months. The fix should also drastically improve the performance of libraries like ActiveRecord-JDBC, which is extremely Java-integration-heavy.
real 0m29.765s
user 0m28.843s
sys 0m2.169s
without ObjectSpace:
real 0m24.984s
user 0m23.559s
sys 0m1.757s
Another area that's been painful was installing Rails with all docs. It used to take over an hour, but now it's under *seven minutes*.
I hope those of you who've seen or blogged about performance problems (especially with the aforementioned ActiveRecord-JDBC) will try re-running your tests. This improvement ought to have a very noticeable effect on benchmarks.
Now the only concern I have with the caching is that it's a little coarse; there may be better places to do the caching, or finer-grained items to cache against. And we could probably pre-fill the cache with some likely candidates. But an improvement like this outweighs those concerns, so it's been committed...and there's bound to be similar improvements as well.
Boy oh boy is that low-hanging fruit looking ripe.
Friday, January 19, 2007
Velocity, F3, Grizzly on Rails, JParseTree
Roundup!
Martin Fowler enjoys using JRuby with Velocity
Jean Lazarou creates an F3 clone with JRuby
Ashish Sahni posts a walkthrough for JRuby on Rails under Grizzly
Werner Shuster releases JParseTree, sexp-based JRuby parse tree generator.
JRuby, JRuby, JRuby.
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?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.
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.<=>
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.rbYes, that's nearly double the performance of the C implementation of Ruby. And this is absolutely real.
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)
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'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.
def bar
baz { puts "hello" }
end
def baz
yield
end
Quite simple, really!
So then an example of the precious, precious JRuby compiler:
$ cat fib_recursive.rbAgain, about twice as fast as Ruby 1.8.5 for this particular benchmark.
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
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 $ jrubycIt'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:
Usage: jrubyc <filename> [<dest>]
~/NetBeansProjects/jruby $ cat samples/fib.rbAt 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.
# 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
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:tmpHuzzah! Compilation!
~/NetBeansProjects/jruby $ java samples/fib
6765
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.rbThe 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...
Error -- Not compileable: Can't compile node: ModuleNode[]
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:
- A method is called. We'll name it "foo"
- foo's code is written in Ruby, so it's just a sequence of AST nodes to be interpreted
- we interpret foo's nodes, but each time we increment a counter. When the counter reaches some number (currently 50), the compiler kicks in
- if the code can't be compiled, we continue to interpretation, but we set a flag and never try to compile again
- if the code can be compiled, we save the generated code and use it for all future invocations
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.rbHere 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.
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) }
Once the JRuby JIT's been wired up, we can simply run the code as normal:
~/NetBeansProjects/jruby $ jruby test/bench/bench_fib_recursive.rbYou 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.
compiled: Object.fib_ruby
2.877000 0.000000 2.877000 ( 2.876000)
2.955000 0.000000 2.955000 ( 2.955000)
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.rbThe future's looking pretty bright.
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)
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:- I started converting the core plugins to C++ pure virtual classes and implementations, to allow for a more componentized system
- 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:- Ultimate Mortal Kombat 3 (in an old Atari Rampart cabinet)
- Killer Instinct 2 (in a KI1 cabinet; the sound ROMs are corrupt, so they need a refresh)
- Killer Instinct 1 (original cabinet; not functional at the moment)
- Mortal Kombat 2 (board only)
- Mortal Kombat 1 (board only)
- Teenage Mutant Ninja Turtles (in an old Taito cabinet with no volume control so it's freaking loud)
- Asteroids (yes, the original, in great working condition; however it's in a Lunar Lander cabinet, of which only a few thousand were ever made. Definitely the gem of the collection).
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
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):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.
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
method dispatch, test (with method calls):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.
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
So then, on to the good old fib tests.
recursive fib: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.
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
iterative fib: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.
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
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.