Ruby Hacking Guide

Final Chapter: Ruby’s future

Issues to be addressed

ruby isn’t ‘completely finished’ software. It’s still being developed, there are still a lot of issues. Firstly, we want to try removing inherent problems in the current interpreter.

The order of the topics is mostly in the same order as the chapters of this book.

Performance of GC

The performance of the current GC might be “not notably bad, but not notably good”. “not notably bad” means “it won’t cause troubles in our daily life”, and “not notably good” means “its downside will be exposed under heavy load”. For example, if it is an application which creates plenty of objects and keeps holding them, its speed would slow down radically. Every time doing GC, it needs to mark all of the objects, and furthermore it would becomes to need to invoke GC more often because it can’t collect them. To counter this problem, Generational GC, which was mentioned in Chapter 5, must be effective. (At least, it is said so in theory.)

Also regarding its response speed, there are still rooms we can improve. With the current GC, while it is running, the entire interpretor stops. Thus, when the program is an editor or a GUI application, sometimes it freezes and stops to react. Even if it’s just 0.1 second, stopping when typing characters would give a very bad impression. Currently, there are few such applications created or, even if exists, its size might be enough small not to expose this problem. However, if such application will actually be created in the future, there might be the necessity to consider Incremental GC.

Implementation of parser

As we saw in Part 2, the implementation of ruby parser has already utilized @yacc@’s ability to almost its limit, thus I can’t think it can endure further expansions. It’s all right if there’s nothing planned to expand, but a big name “keyword argument” is planned next and it’s sad if we could not express another demanded grammar because of the limitation of yacc.

Reuse of parser

Ruby’s parser is very complex. In particular, dealing with around lex_state seriously is very hard. Due to this, embedding a Ruby program or creating a program to deal with a Ruby program itself is quite difficult.

For example, I’m developing a tool named racc, which is prefixed with R because it is a Ruby-version yacc. With racc, the syntax of grammar files are almost the same as yacc but we can write actions in Ruby. To do so, it could not determine the end of an action without parsing Ruby code properly, but “properly” is very difficult. Since there’s no other choice, currently I’ve compromised at the level that it can parse “almost all”.

As another example which requires analyzing Ruby program, I can enumerate some tools like indent and lint, but creating such tool also requires a lot efforts. It would be desperate if it is something complex like a refactoring tool.

Then, what can we do? If we can’t recreate the same thing, what if @ruby@’s original parser can be used as a component? In other words, making the parser itself a library. This is a feature we want by all means.

However, what becomes problem here is, as long as yacc is used, we cannot make parser reentrant. It means, say, we cannot call yyparse() recursively, and we cannot call it from multiple threads. Therefore, it should be implemented in the way of not returning control to Ruby while parsing.

Hiding Code

With current ruby, it does not work without the source code of the program to run. Thus, people who don’t want others to read their source code might have trouble.

Interpretor Object

Currently each process cannot have multiple ruby interpretors, this was discussed in Chapter 13. If having multiple interpretors is practically possible, it seems better, but is it possible to implement such thing?

The structure of evaluator

Current eval.c is, above all, too complex. Embedding Ruby’s stack frames to machine stack could occasionally become the source of trouble, using setjmp() longjmp() aggressively makes it less easy to understand and slows down its speed. Particularly with RISC machine, which has many registers, using setjmp() aggressively can easily cause slowing down because setjmp() set aside all things in registers.

The performance of evaluator

ruby is already enough fast for ordinary use. But aside from it, regarding a language processor, definitely the faster is the better. To achieve better performance, in other words to optimize, what can we do? In such case, the first thing we have to do is profiling. So I profiled.

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 20.25      1.64     1.64  2638359     0.00     0.00  rb_eval
 12.47      2.65     1.01  1113947     0.00     0.00  ruby_re_match
  8.89      3.37     0.72  5519249     0.00     0.00  rb_call0
  6.54      3.90     0.53  2156387     0.00     0.00  st_lookup
  6.30      4.41     0.51  1599096     0.00     0.00  rb_yield_0
  5.43      4.85     0.44  5519249     0.00     0.00  rb_call
  5.19      5.27     0.42   388066     0.00     0.00  st_foreach
  3.46      5.55     0.28  8605866     0.00     0.00  rb_gc_mark
  2.22      5.73     0.18  3819588     0.00     0.00  call_cfunc

This is a profile when running some application but this is approximately the profile of a general Ruby program. rb_eval() appeared in the overwhelming percentage being at the top, after that, in addition to functions of GC, evaluator core, functions that are specific to the program are mixed. For example, in the case of this application, it takes a lot of time for regular expression match (ruby_re_match).

However, even if we understood this, the question is how to improve it. To think simply, it can be archived by making rb_eval() faster. That said, but as for ruby core, there are almost not any room which can be easily optimized. For instance, apparently “tail recursive → goto conversion” used in the place of NODE_IF and others has already applied almost all possible places it can be applied. In other words, without changing the way of thinking fundamentally, there’s no room to improve.

The implementation of thread

This was also discussed in Chapter 19. There are really a lot of issues about the implementation of the current ruby’s thread. Particularly, it cannot mix with native threads so badly. The two great advantages of @ruby@’s thread, (1) high portability (2) the same behavior everywhere, are definitely incomparable, but probably that implementation is something we cannot continue to use eternally, isn’t it?

`ruby` 2

Subsequently, on the other hand, I’ll introduce the trend of the original `ruby`, how it is trying to counter these issues.

Rite

At the present time, ruby’s edge is 1.6.7 as the stable version and 1.7.3 as the development version, but perhaps the next stable version 1.8 will come out in the near future. Then at that point, the next development version 1.9.0 will start at the same time. And after that, this is a little irregular but 1.9.1 will be the next stable version.

stable development when to start
1.6.x 1.7.x 1.6.0 was released on 2000-09-19
1.8.x 1.9.x probably it will come out within 6 months
1.9.1~ 2.0.0 maybe about 2 years later

And the next-to-next generational development version is `ruby` 2, whose code name is Rite. Apparently this name indicates a respect for the inadequacy that Japanese cannot distinguish the sounds of L and R.

What will be changed in 2.0 is, in short, almost all the entire core. Thread, evaluator, parser, all of them will be changed. However, nothing has been written as a code yet, so things written here is entirely just a “plan”. If you expect so much, it’s possible it will turn out disappointments. Therefore, for now, let’s just expect slightly.

The language to write

Firstly, the language to use. Definitely it will be C. Mr. Matsumoto said to `ruby-talk`, which is the English mailing list for Ruby,

I hate C++.

So, C++ is most unlikely. Even if all the parts will be recreated, it is reasonable that the object system will remain almost the same, so not to increase extra efforts around this is necessary. However, chances are good that it will be ANSI C next time.

GC

Regarding the implementation of GC, the good start point would be `Boehm GC`\footnote{Boehm GC `http://www.hpl.hp.com/personal/Hans_Boehm/gc`}. Bohem GC is a conservative and incremental and generational GC, furthermore, it can mark all stack spaces of all threads even while native threads are running. It’s really an impressive GC. Even if it is introduced once, it’s hard to tell whether it will be used perpetually, but anyway it will proceed for the direction to which we can expect somewhat improvement on speed.

Parser

Regarding the specification, it’s very likely that the nested method calls without parentheses will be forbidden. As we’ve seen, `command_call` has a great influence on all over the grammar. If this is simplified, both the parser and the scanner will also be simplified a lot. However, the ability to omit parentheses itself will never be disabled.

And regarding its implementation, whether we continue to use `yacc` is still under discussion. If we won’t use, it would mean hand-writing, but is it possible to implement such complex thing by hand? Such anxiety might left. Whichever way we choose, the path must be thorny.

Evaluator

The evaluator will be completely recreated. Its aims are mainly to improve speed and to simplify the implementation. There are two main viewpoints:

First, removing recursive calls of `rb_eval()`. The way to remove is, maybe the most intuitive explanation is that it’s like the “tail recursive → `goto` conversion”. Inside a single `rb_eval()`, circling around by using `goto`. That decreases the number of function calls and removes the necessity of `setjmp()` that is used for `return` or `break`. However, when a function defined in C is called, calling a function is inevitable, and at that point `setjmp()` will still be required.

Bytecode is, in short, something like a program written in machine language. It became famous because of the virtual machine of Smalltalk90, it is called bytecode because each instruction is one-byte. For those who are usually working at more abstract level, byte would seem so natural basis in size to deal with, but in many cases each instruction consists of bits in machine languages. For example, in Alpha, among a 32-bit instruction code, the beginning 6-bit represents the instruction type.

The advantage of bytecode interpretors is mainly for speed. There are two reasons: Firstly, unlike syntax trees, there’s no need to traverse pointers. Secondly, it’s easy to do peephole optimization.

And in the case when bytecode is saved and read in later, because there’s no need to parse, we can naturally expect better performance. However, parsing is a procedure which is done only once at the beginning of a program and even currently it does not take so much time. Therefore, its influence will not be so much.

If you’d like to know about how the bytecode evaluator could be, `regex.c` is worth to look at. For another example, Python is a bytecode interpretor.

Thread

Regarding thread, the thing is native thread support. The environment around thread has been significantly improved, comparing with the situation in 1994, the year of Ruby’s birth. So it might be judged that we can get along with native thread now.

Using native thread means being preemptive also at C level, thus the interpretor itself must be multi-thread safe, but it seems this point is going to be solved by using a global lock for the time being.

Additionally, that somewhat arcane “continuation”, it seems likely to be removed. `ruby`’s continuation highly depends on the implementation of thread, so naturally it will disappear if thread is switched to native thread. The existence of that feature is because “it can be implemented” and it is rarely actually used. Therefore there might be no problem.

M17N

In addition, I’d like to mention a few things about class libraries. This is about multi-lingualization (M17N for short). What it means exactly in the context of programming is being able to deal with multiple character encodings.

`ruby` with Multi-lingualization support has already implemented and you can obtain it from the `ruby_m17m` branch of the CVS repository. It is not absorbed yet because it is judged that its specification is immature. If good interfaces is designed, it will be absorbed at some point in the middle of 1.9.

IO

The `IO` class in current Ruby is a simple wrapper of `stdio`, but in this approach,

these two points cause complaints. Therefore, it seems Rite will have its own `stdio`.

Ruby Hacking Guide

So far, we’ve always acted as observers who look at `ruby` from outside. But, of course, `ruby` is not a product which displayed in a showcase. It means we can influence it if we take an action for it. In the last section of this book, I’ll introduce the suggestions and activities for `ruby` from community, as a farewell gift for Ruby Hackers both at present and in the future.

Generational GC

First, as also mentioned in Chapter 5, the generational GC made by Mr. Kiyama Masato. As described before, with the current patch,

these points are problems, but here I’d like to highly value it because, more than anything else, it was the first large non-official patch.

Oniguruma

The regular expression engine used by current Ruby is a remodeled version of GNU regex. That GNU regex was in the first place written for Emacs. And then it was remodeled so that it can support multi-byte characters. And then Mr. Matsumoto remodeled so that it is compatible with Perl. As we can easily imagine from this history, its construction is really intricate and spooky. Furthermore, due to the LPGL license of this GNU regex, the license of `ruby` is very complicated, so replacing this engine has been an issue from a long time ago.

What suddenly emerged here is the regular expression engine “Oniguruma” by Mr. K. Kosako. I heard this is written really well, it is likely being absorbed as soon as possible.

You can obtain Oniguruma from the `ruby`’s CVS repository in the following way.

% cvs -d :pserver:anonymous@cvs.ruby-lang.org:/src co oniguruma

ripper

Next, ripper is my product. It is an extension library made by remodeling `parse.y`. It is not a change applied to the `ruby`’s main body, but I introduced it here as one possible direction to make the parser a component.

It is implemented with kind of streaming interface and it can pick up things such as token scan or parser’s reduction as events. It is put in the attached CD-ROM \footnote{ripper:`archives/ripper-0.0.5.tar.gz` of the attached CD-ROM}, so I’d like you to give it a try. Note that the supported grammar is a little different from the current one because this version is based on `ruby` 1.7 almost half-year ago.

I created this just because “I happened to come up with this idea”, if this is accounted, I think it is constructed well. It took only three days or so to implement, really just a piece of cake.

A parser alternative

This product has not yet appeared in a clear form, there’s a person who write a Ruby parser in C++ which can be used totally independent of `ruby`. (`[ruby-talk:50497]`).

JRuby

More aggressively, there’s an attempt to rewrite entire the interpretor. For example, a Ruby written in Java, Ruby\footnote{JRuby `http://jruby.sourceforge.net`}, has appeared. It seems it is being implemented by a large group of people, Mr. Jan Arne Petersen and many others.

I tried it a little and as my reviews,

perhaps I could say at least these things. Regarding the last one “slow”, its degree is, the execution time it takes is 20 times longer than the one of the original `ruby`. Going this far is too slow. It is not expected running fast because that Ruby VM runs on Java VM. Waiting for the machine to become 20 times faster seems only way.

However, the overall impression I got was, it’s way better than I imagined.

NETRuby

If it can run with Java, it should also with C#. Therefore, a Ruby written in C# appeared, “NETRuby\footnote{NETRuby `http://sourceforge.jp/projects/netruby/`}”. The author is Mr. arton.

Because I don’t have any .NET environment at hand, I checked only the source code, but according to the author,

such things are the problems. But `instance_eval` is in effect (astounding!).

How to join `ruby` development

`ruby`’s developer is really Mr. Matsumoto as an individual, regarding the final decision about the direction `ruby` will take, he has the definitive authority. But at the same time, `ruby` is an open source software, anyone can join the development. Joining means, you can suggest your opinions or send patches. The below is to concretely tell you how to join.

In `ruby`‘s case, the mailing list is at the center of the development, so it’s good to join the mailing list. The mailing lists currently at the center of the community are three: `ruby-list`, `ruby-dev`, `ruby-talk`. `ruby-list` is a mailing list for “anything relating to Ruby” in Japanese. `ruby-dev` is for the development version `ruby`, this is also in Japanese. `ruby-talk` is an English mailing list. The way to join is shown on the page “mailing lists” at Ruby’s official site \footnote{Ruby’s official site: `http://www.ruby-lang.org/ja/`}. For these mailing lists, read-only people are also welcome, so I recommend just joining first and watching discussions to grasp how it is.

Though Ruby’s activity started in Japan, recently sometimes it is said “the main authority now belongs to `ruby-talk`”. But the center of the development is still `ruby-dev`. Because people who has the commit right to `ruby` (e.g. core members) are mostly Japanese, the difficulty and reluctance of using English naturally lead them to `ruby-dev`. If there will be more core members who prefer to use English, the situation could be changed, but meanwhile the core of `ruby`’s development might remain `ruby-dev`.

However, it’s bad if people who cannot speak Japanese cannot join the development, so currently the summary of `ruby-dev` is translated once a week and posted to `ruby-talk`. I also help that summarising, but only three people do it in turn now, so the situation is really harsh. The members to help summarize is always in demand. If you think you’re the person who can help, I’d like you to state it at `ruby-list`.

And as the last note, only its source code is not enough for a software. It’s necessary to prepare various documents and maintain web sites. And people who take care of these kind of things are always in short. There’s also a mailing list for the document-related activities, but as the first step you just have to propose “I’d like to do something” to `ruby-list`. I’ll answer it as much as possible, and other people would respond to it, too.

Finale

The long journey of this book is going to end now. As there was the limitation of the number of pages, explaining all of the parts comprehensively was impossible, however I told everything I could tell about the `ruby`‘s core. I won’t add extra things any more here. If you still have things you didn’t understand, I’d like you to investigate it by reading the source code by yourself as much as you want.