Skip to content

sisshiki1969/ruruby

Repository files navigation

ruruby rurubycodecov

An alternative Ruby implementation by Rust.

Features

  • Purely implemented with Rust.
  • No dependency on any other Ruby implementation such as CRuby(MRI), mruby, .. etc.
  • Hand-written original parser.
  • Virtual machine execution.
  • Simple mark & sweep garbage collector is implemented.
  • Supporting x86/posix, arm64/macos, x86/windows (thanks @jtran and @dmtaub!) . 64-bit arch only.

Related articles (sorry, currently only in Japanese)

Qiita: Rust でつくる(つくれるかもしれない)Ruby (You can (possibly) make Ruby.)
Qiita: ruruby: Rust でつくっている Ruby (Making Ruby with Rust)
SpeakerDeck: Rust でつくるガーベジコレクタ (Garbage collector written in Rust)

Implementation status

See Wiki.

Optcarrot benchmark

benchmark CRuby ruruby rate
optcarrot 56.09 ± 0.13 fps 34.80 ± 0.08 fps x 1.61
optcarrot --opt 130.53 ± 0.82 fps 101.85 ± 1.08 fps x 1.28

To check other benchmark results, see Wiki.

You can see the results of optcarrot benchmark for ruruby and other Ruby implementations here.

How to run ruruby

To build ruruby, You'll need installation of Rust. Please be aware that only nightly version of Rust works for ruruby.

To run ruby program file on ruruby,

% cargo run tests/sample.rb

or

% cargo run --release -- tests/sample.rb

You can launch irb-like interactive shell, omitting file name.

% cargo run

There are some useful options for analysis and development. Use features flag of cargo.

trace option: bytecode trace execution

% cargo run --features trace -- tests/sample.rb
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/ruruby tests/sample.rb`
+++> MethodId(552) Method[unnamed] "/home/monochrome/ruruby/tests/sample.rb"
--------invoke new context------------------------------------------
Stack context:0x55be3bec7d00 outer:None prev_stack_len:0
  iseq: Ref(0x55be3bedbc90)
  self: #<Object:0x00007f94e2b40140>
  lvar(0): w nil
  delegate: None
--------------------------------------------------------------------
   0: CONST_VAL "world"                        tmp: 0    stack: 0   top:
   5: SET_LOCAL 'w'                            tmp: 0    stack: 1   top: "world"
   a: CONST_VAL "Hello "                       tmp: 0    stack: 0   top:
   f: GET_LOCAL 'w'                            tmp: 0    stack: 1   top: "Hello "
  14: TO_S                                     tmp: 0    stack: 2   top: "world"
  15: CONST_VAL "!"                            tmp: 0    stack: 2   top: "world"
  1a: CONCAT_STR 3 items                       tmp: 0    stack: 3   top: "!"
  1f: O_SEND_SLF 'puts' args:1 block:None      tmp: 0    stack: 1   top: "Hello world!"
+++> BuiltinFunc puts
Hello world!
<+++ Ok(nil)
  2e: RETURN                                   tmp: 0    stack: 1   top: nil
<+++ Ok(nil)

emit-iseq option: dump bytecode

% cargo run --features emit-iseq -- tests/sample.rb
   Compiling ruruby v0.3.1 (/home/monochrome/ruruby)
    Finished dev [unoptimized + debuginfo] target(s) in 9.01s
     Running `target/debug/ruruby tests/sample.rb`
-----------------------------------------
[MethodId(552)] Method: <unnamed> opt:true
local var: 0:w
block: None
  00000 CONST_VAL "world"
  00005 SET_LOCAL 'w'
  0000a CONST_VAL "Hello "
  0000f GET_LOCAL 'w'
  00014 TO_S
  00015 CONST_VAL "!"
  0001a CONCAT_STR 3 items
  0001f O_SEND_SLF 'puts' args:1 block:None
  0002e RETURN
Hello world!

perf option: performance analysis per VM instruction

% cargo run --release --features perf -- tests/app_mandelbrot.rb > /dev/null
    Finished release [optimized] target(s) in 25.75s
     Running `target/release/ruruby tests/app_mandelbrot.rb`
+-------------------------------------------+
| Performance stats for inst:               |
| Inst name         count    %time  ns/inst |
+-------------------------------------------+
  PUSH_VAL          5385K     5.57       37
  PUSH_NIL          3948K     3.97       36
  CONST_VAL             2     0.00      100
  SET_LOCAL          320K     0.39       43
  GET_LOCAL          620K     0.66       38
  SET_DYNLOCAL      4265K     4.69       39
  GET_DYNLOCAL        16M    17.25       36
  O_SEND            3925K     6.16       56
  O_SEND_SLF         480K     0.93       69
  O_SEND_N           160K     0.30       66
  O_SEND_SLF_N        20K     0.04       67
  DUP                 20K     0.03       51
  CONCAT_STR            1     0.00      400
  TO_S                  2     0.00      600
  DEF_METHOD            1     0.00      300
  RETURN            4032K     6.82       60
  MRETURN             96K     0.20       74
  ADD               3905K     6.26       57
  SUB                320K     0.42       47
  MUL               4225K     6.55       55
  DIV                320K     0.41       45
  SHL                160K     0.22       48
  BIT_OR             160K     0.20       44
  ADDI               160K     0.20       44
  SUBI               140K     0.17       44
  JMP_F_EQ           140K     0.19       49
  JMP_F_GT          3905K     4.57       41
  JMP_F_EQI          160K     0.22       49
  GC                  697     0.75    38699
  EXTERN              12M    32.82       96

Instruction name, total execution count, percentage in the total execution time, and execution time per single instruction are shown.
* GC means garbage collection.
** EXTERN means exectution of native methods.