Monday, September 24, 2007

XRuby 0.3.1 released

XRuby 0.3.1 is released today and you can download the latest version here:
http://code.google.com/p/xruby/downloads/list

The major changes in this version are:

1. Ruby standard libraries are now pre-compiled and the compilation results (java bytecode) are shipped as part of the xruby.jar. This makes deployment really easy, as there is one jar file to deal with. Pre-compiled libraries means it isn't necessary to compile these code every time by the user which is helpful to performance.

The size of the final jar may cause some concerns: while the number of ruby stdlib supported by the current version of XRuby is still limited, the size of the compiled stdlib has already reached 2,000 KB (the entire xruby-0.3.1.jar is over 4,000 KB). Maybe we will ship ruby stdlib in a separated jar files in the future.

2. Use annotation and code generation to bind Java level method to Ruby level method. We started to make this change in 0.3.0 and finished it in 0.3.1. It helped us to get rid of lots of redundant code. For more information about this change, please read our last announcement and dreamhead's articles(1, 2) on his blogs (in Chinese).

Thank everyone who has contributed to this release.

Tuesday, August 7, 2007

xruby 0.3.0 released

I am pleased to announce that XRuby 0.3.0 is released:
http://code.google.com/p/xruby/downloads/list

We have fixed lots of bugs and made significant improvement in the code.

Changes from 0.2.1 to 0.3.0:
1. Use annotation and code generation to bind Java level method to Ruby level method (I will talk more about this later).

2. More unit tests passed. We have not eliminated all test failures in test/ruby. But as most of the failures are caused by the implementation of builtin libraries, we will be able to fixed them soon in 0.4.0.

Changes from 0.2.0 to 0.2.1:
1) Dreamhead optimized method/block calls for methods with zero/one
arguments. It makes our performance even better.

2) ZhangYu improved Java integration significantly, he also created a wiki page with lots of good examples:
http://code.google.com/p/xruby/wiki/JavaIntegration

3) Mechiland and I made more ruby unit tests pass.

The most significant change of 0.3.0 is the using of annotation and code generation to bind Java level method to ruby level method. The idea was inspired by the discussions about Java 5 on jruby's maillist, and dreamhead turned it into reality quickly.

As we know, a Ruby method does a little bit more than a Java method. So if we have a method like this in Java:

public class RubyString {
public RubyFloat to_f() {
...
}
}
To turn it (RubyString.to_f) into a Ruby level method, we have to add a few more code to 'wrap' into a class (extends RubyMethod) and 'register' it (defineMethod), e.g:
public class String_to_f extends RubyNoArgMethod {
protected RubyValue run(RubyValue receiver, RubyBlock block) {
return ((RubyString)receiver).to_f();
}
}
...
RubyRuntime.StringClass.defineMethod("to_f", new String_to_f());
For every method, we need to write similar code and it is not fun to repeat yourself. In 0.3.0, we no longer have to do this anymore. As as long as you add annotation like this:
@RubyLevelClass(name="String")
public class RubyString {
@RubyLevelMethod(name="to_f")
public RubyFloat to_f() {
...
}
}
XRuby will turn it into a Ruby level method automatically (using ASM to generate Java bytecode).

I have not used Java 5's annotation feature before, but this looks like an very elegant solution.

Thank everyone who has contributed to this release.

Tuesday, June 12, 2007

ruby -y

For people who are interested in how ruby's yacc parser works, there is an undocumented command line option("-y") that may be helpful. It will display a trace of the parser's operations.

To use it, you need to clear your RUBYOPT environment variable to NOT use "rubygems" (this will break some ruby applications), otherwise it will make too much noise.

Here is an example:


$ruby -y -e "a=1"
Starting parse
Entering state 0
Reducing stack by rule 1 (line 328), -> @1
Stack now 0
Entering state 2
Reading a token: Next token is token tIDENTIFIER ()
Shifting token tIDENTIFIER, Entering state 34
Reading a token: Next token is token '=' ()
Reducing stack by rule 418 (line 2146), tIDENTIFIER -> variable
Stack now 0 2
Entering state 90
Next token is token '=' ()
Reducing stack by rule 83 (line 827), variable -> lhs
Stack now 0 2
Entering state 73
Next token is token '=' ()
Shifting token '=', Entering state 315
Reading a token: Next token is token tINTEGER ()
Shifting token tINTEGER, Entering state 40
Reducing stack by rule 414 (line 2134), tINTEGER -> numeric
Stack now 0 2 73 315
Entering state 89
Reducing stack by rule 376 (line 1899), numeric -> literal
Stack now 0 2 73 315
Entering state 79
Reducing stack by rule 267 (line 1421), literal -> primary
Stack now 0 2 73 315
Entering state 75
Reading a token: Next token is token '\n' ()
Reducing stack by rule 217 (line 1199), primary -> arg
Stack now 0 2 73 315
Entering state 488
Next token is token '\n' ()
Reducing stack by rule 173 (line 953), lhs '=' arg -> arg
Stack now 0 2
Entering state 74
Next token is token '\n' ()
Reducing stack by rule 40 (line 616), arg -> expr
Stack now 0 2
Entering state 64
Next token is token '\n' ()
Reducing stack by rule 34 (line 596), expr -> stmt
Stack now 0 2
Entering state 63
Next token is token '\n' ()
Reducing stack by rule 6 (line 381), stmt -> stmts
Stack now 0 2
Entering state 62
Next token is token '\n' ()
Shifting token '\n', Entering state 216
Reducing stack by rule 496 (line 2429), '\n' -> term
Stack now 0 2 62
Entering state 220
Reducing stack by rule 497 (line 2432), term -> terms
Stack now 0 2 62
Entering state 300
Reading a token: Now at end of input.
Reducing stack by rule 489 (line 2416), terms -> opt_terms
Stack now 0 2 62
Entering state 299
Reducing stack by rule 4 (line 373), stmts opt_terms -> compstmt
Stack now 0 2
Entering state 61
Reducing stack by rule 2 (line 328), @1 compstmt -> program
Stack now 0
Entering state 1
Now at end of input.


XRuby's ANTLR parser does not have this option. But as ANRLE produces human readable code, you can just read the generated code or step through it in a debugger to learn how it works.

Tuesday, May 15, 2007

XRuby 0.2.0 released

I am glad to announce that XRuby 0.2.0 is now available for download at:
http://code.google.com/p/xruby/downloads/list

Here is a summary of major changes in this release:

1. Beanworms started to work on the debugging support and we now have a basic debugger in trunk.
2. Zhang Yu and Haofei Wang added/fixed lots of builtin methods
3. Dreamhead rewrote RubySymbol as the old one was plain wrong
4. As test::unit works on XRuby, we imported unit tests from c ruby (test\ruby) to test our implementation. The result was not very impressive so far, less than half of the tests passed. We are planning to make all tests pass in the coming weeks.
5. Meanwhile, Femto is leading our efforts to create an ANTLR v3 based ruby parser in another branch. Upgrading our parser from ANTLR v2 to v3 is indeed challenging, but he has make very impressive progress.

Thank you everybody who contributed to this release!

Tuesday, April 17, 2007

InfoQ article on XRuby

Werner Schuster from InfoQ has a very nice article about XRuby, you can read it here.

UPDATE 04/27/2007: InfoQ China had an interview with our core developer Dreamhead. You can read it here (in Chinese). And congratulations to Dreamhead for joining Thoughtworks.

Tuesday, April 10, 2007

XRuby 0.1.4 released

I am glad to announce that XRuby 0.1.4 is now available for download at: http://code.google.com/p/xruby/downloads/list

The most significant change in this release is: one of our team member, dreamhead, single-handly created a new runtime (the core of the system that manages ruby types, methods...) to make it very close to the classic C ruby, and therefore fixed several compatibility issues with C ruby. As a translator of the Chinese version Ruby Hacking Guide, dreamhead has very good knowledge of the ruby internal and gives a great lift on the XRuby's development.

As we have learned rewriting is very difficult, we learned it again in a hard way. Dreamhead wrote most of code last year, several months before 0.1.0 release. First we tried to make a wholesale replacement of the runtime, but problems were here and there and lots of unit tests broke. Since we do not want to have a broken system in the SVN trunk, we decided to take 'refactoring' approach and merge changes step by step. It is still very hard, but dreamhead made lots of efforts and finished the merge yesterday.

Actually it may take less time if we insist on following the 'rewrite' approach (replace all the old code and fix broken tests), but the benefit of 'refactoring' is significant: it gives us a stable code base so that we made several releases during this big change.

There are more exciting news behind the scene now: Beanworms is working on the debugger support, and Femto and Yuesefa have showed interest to migrate our ruby parser to ANTLR 3.0.

Thank you for people who helps us on making this release. It has been a great learning experience working with the project.

Sunday, March 11, 2007

Ruby builtin in pure Ruby

[Update 03/12/2007 If you know how to implement Interge#times in pure ruby and make it have the same behavior as Ruby 1.8.5, please let me know. Thank you!]

One of the best things I love about rubinius project is: their developers try to keep the dependency on system language (C in their case) minimal, and they take it seriously. Take a look of http://code.fallingsnow.net/svn/rubinius/trunk/kernel/core/, you will find out lots of buitlin libraries are implemented in pure ruby, even lots of string functions.

Pure ruby builtin has some drawbacks, though. One is performance penalty, the other is potential side effects. For example, basically Integer#times can be implemented as simple as this in pure ruby:


class Integer
def times
i = 0
while i < self
yield i
i += 1;
end
self
end
end


But this version of Integer#times does not work exactly the same as Ruby 1.8.5. If an user want to override Fixnum#+ (this may never happen in real life):


class Fixnum
def + x
return 9999
end
end


Our Integer#times's behavior will change, while Ruby 1.8.5 won't. That is because Ruby 1.8.5's implementation (int_dotimes() in Numeric.c) optimizes for Fixnum: it does not call '+' method dynamically for Fixnum, instead, it just increases the integer directly. If you want to implement this method as same as Ruby 1.8.5 then you have to write code in system language.

This kind of optimization is all over the place in c ruby. I am not clear about its motivation, but I guess performance is one of the reasons. For example, "30000000.times {|x|}" is about twice faster than "i = 0; while i < 30000000; i +=1; end" in Ruby 1.8.5.

Difference people may have different opinions on what the 'right' behavior should be. As for me, I like the behavior of the pure ruby implementation better.

Tuesday, March 6, 2007

XRuby is faster than Ruby 1.8.5 in most benchmarks

Two weeks ago, Antonio Cangiano compared the performance of different ruby implementations using Ruby 1.9 (YARV)'s benchmark suite. His numbers got me thinking: all alternative implementations performed badly -- most are even way slower than ruby 1.8.5. Does it signal that JVM and .NET are bad platform for Ruby language?

With this doubt I tried the benchmark with XRuby. XRuby is a ruby compiler. Unlike other implementations, it generates Java bytecode that run directly on JVM. But at first the numbers are not impressive: the 0.1.2 version is still slower than Ruby 1.8.5 in most of the cases.

Maybe I should mention that the XRuby team had done virtually nothing for performance before, and we would avoid optimization as long as possible if it makes our code complicated. But after doing some measurements, it turns out our bad performance are largely caused by a logic 'error': as we know Ruby Fixnum can not have singleton methods, but in 0.1.2 it still lookup an empty method table. And along with some bad code practices (iterating an empty ArrayList without checking if it is empty first etc), it makes method lookup much slower than it should be.

I fixed the problem by adding about 10 lines of code, and got great result: In most benchmarks, XRuby 0.1.3 is faster than Ruby 1.8.5. For some, faster in a significant way. There are still some tests in which we are slower, but it looks like caused by poorly implemented builtin.

The following table shows the benchmark result for XRuby 0.1.3. The best part is: we did it without a method cache. YARV is still faster than XRuby, but we have lots of room to improve too.

>java -Xmx512m -jar xruby-0.1.3.jar benchmark\run.rb

TestRuby 1.8.5XRuby 0.1.3
bm_app_answer.rbfailfail
bm_app_factorial.rbfailfail
bm_app_fib.rb20.0212.29
bm_app_mandelbrot.rb7.0998.252
bm_app_pentomino.rb289.8538.5
bm_app_raise.rb4.8463.986
bm_app_strconcat.rb5.8983.234
bm_app_tak.rb26.1422.12
bm_app_tarai.rb20.8918.35
bm_loop_times.rb14.2819.30
bm_loop_whileloop.rb26.0319.27
bm_loop_whileloop2.rb5.2574.786
bm_so_ackermann.rbfailfail
bm_so_array.rb19.1746.84
bm_so_concatenate.rb5.7279.684
bm_so_count_words.rb2.94445.50
bm_so_exception.rb9.7937.399
bm_so_lists.rb3.66624.59
bm_so_matrix.rb6.2498.452
bm_so_nested_loop.rb15.1713.45
bm_so_object.rb21.497.991
bm_so_random.rb6.1695.888
bm_so_sieve.rb2.0422.753
bm_vm1_block.rb64.5738.69
bm_vm1_const.rb47.4725.57
bm_vm1_ensure.rb45.5420.01
bm_vm1_length.rb55.5040.89
bm_vm1_rescue.rb39.6120.64
bm_vm1_simplereturn.rb56.0229.06
bm_vm1_swap.rb76.3530.52
bm_vm2_array.rb19.348.532
bm_vm2_method.rb33.7219.63
bm_vm2_poly_method.rb45.2320.62
bm_vm2_poly_method_ov.rb12.648.261
bm_vm2_proc.rb21.0817.86
bm_vm2_regexp.rb13.0930.87
bm_vm2_send.rb11.7115.75
bm_vm2_super.rb13.927.510
bm_vm2_unif1.rb11.308.292
bm_vm2_zsuper.rb15.717.740
bm_vm3_thread_create_join.rb0.1101.331


* The test environment is Intel Pentium M 1G CPU, 1G Memory, Windows XP SP2, Java 1.5.0_09.

Wednesday, February 28, 2007

XRuby 0.1.2 released

XRuby 0.1.2 is now available for download from the project download page: http://code.google.com/p/xruby/downloads/list

This latest release contains more built-in libraries and a few bug fixes for the compiler. We expect to release a new stable version every month.

Last week, Antonio Cangiano did a very interesting job to compare the performance of different ruby implementations using YARV's benchmark suite. XRuby is not included in his test as Antonio may not know our project exists. For people who are interested, here is the XRuby's performance data (due to my limited resource, only ruby 1.8.5 interpreter was compared):

>java -Xmx512m -jar xruby-0.1.2.jar benchmark\run.rb

TestRuby 1.8.5XRuby 0.1.2
bm_app_answer.rbfailfail
bm_app_factorial.rbfailfail
bm_app_fib.rb20.4233.84
bm_app_mandelbrot.rb7.27015.33
bm_app_pentomino.rb294.2956.9
bm_app_raise.rb4.9064.597
bm_app_strconcat.rb5.9175.858
bm_app_tak.rb26.4638.95
bm_app_tarai.rb21.0835.51
bm_loop_times.rb14.8393.55
bm_loop_whileloop.rb26.2676.5
bm_loop_whileloop2.rb5.32716.15
bm_so_ackermann.rbfailfail
bm_so_array.rb19.35300.2
bm_so_concatenate.rb5.86826.58
bm_so_count_words.rb2.25346.43
bm_so_exception.rb9.8939.973
bm_so_lists.rb3.69641.04
bm_so_matrix.rb6.32821.29
bm_so_nested_loop.rb15.3868.20
bm_so_object.rb21.9720.77
bm_so_random.rb6.26915.69
bm_so_sieve.rb1.9925.476
bm_vm1_block.rb65.6798.91
bm_vm1_const.rb48.0582.31
bm_vm1_ensure.rb46.1975.38
bm_vm1_length.rb55.62105.6
bm_vm1_rescue.rb36.4780.91
bm_vm1_simplereturn.rb57.1288.16
bm_vm1_swap.rb76.2185.73
bm_vm2_array.rb18.6419.65
bm_vm2_method.rb33.4135.32
bm_vm2_poly_method.rb45.4247.78
bm_vm2_poly_method_ov.rb12.8125.98
bm_vm2_proc.rb21.5431.23
bm_vm2_regexp.rb13.4052.35
bm_vm2_send.rb11.8731.60
bm_vm2_super.rb13.9820.25
bm_vm2_unif1.rb11.4619.95
bm_vm2_zsuper.rb15.8421.92
bm_vm3_thread_create_join.rb0.1201.402


The test environment is Intel Pentium M 1G CPU, 1G Memory, Windows XP SP2, Java 1.5.0_09. It may be worth to mention we have done almost nothing for optimization, not even a simple method cache. I think it is quite possible for a JVM based ruby compiler to beat the classic Ruby interpreter on performance, while YARV's number is going to be very hard for us to reach.

Sunday, February 18, 2007

On-Ruby interview


Pat Eyler recently interviewed XRuby team, the first episode is now posted on his popular On-Ruby blog: http://on-ruby.blogspot.com/2007/02/serial-xruby-interview-episode-i.html

Monday, January 29, 2007

XRuby 0.1.0 released


[Link of this article: http://xruby.blogspot.com/2007/01/xruby-010-released.html]

Today I am glad to announce the release XRuby 0.1.0 , a ruby compiler which compiles ruby script (.rb) to java bytecode (.class). You can download it from here: http://code.google.com/p/xruby/downloads/list

XRuby 0.1.0 is the first release of XRuby. It is able to pass all tests in samples/test.rb - a decent test suite that comes with ruby installation.

As a compiler, XRuby compiles ruby source code so that your program can run on top of Java Virtual Machine. For example, if you have a ruby script like this:

class MyClass
  def say_hello_three_times
    3.times {puts "hello"}
  end
end

MyClass.new.say_hello_three_times


You can compile it with XRuby (assuming its name is test.rb):

>java -jar xruby-0.1.0.jar -c test.rb

The compiler will generate a test.jar. It can be launched as a regular java application, with the following command:

>java -jar test.jar
hello
hello
hello


For your convenience, you can also run the script directly (without -c option), just like the classic ruby interpreter. Under the hood it compiles the script then run the bytecode:

>java -jar xruby-0.1.0.jar test.rb
hello
hello
hello


While little known, this project has been under active development for one year and eight months. I started the project around mid 2005, and it took me about eight months to write a ruby parser with Antlr, and then one year to implement the compiler and runtime. Some people joined me during the second half of 2006 and made great contribution. It is just the beginning of an exciting journey.

We still have a long way to go before claiming XRuby is a very competent alternative implementation of ruby. And comparing with our peers we lag behind in implementing ruby's built-in libraries and do not have Ruby-Java bridge. Our next focus is to improve this situation and make major ruby libraries and framework work under XRuby. And we hope by end of 2007 you can use XRuby to compile your Ruby On Rails application to a .war file and run it directly on a J2EE server.

We appreciate your feedback. Our development maillist is at: http://groups.google.com/group/xruby-devel.

I also setup a new blog to be used as XRuby's team blog: http://xruby.blogspot.com . While new articles will be cross posted for quite a while, I encourage you to subscribe http://feeds.feedburner.com/xruby if have not done so.

Thank you and have fun.