Ricsin:
RubyにCを埋め込むシステム
Ricsin: A System for “C Mix-in to Ruby”

東京⼤学⼤学院情報理⼯学系研究科創造情報学専攻

笹⽥ 耕⼀ / ささだ こういち
sasada@ci.i.u-tokyo.ac.jp

2008/10/28

Ricsin: RubyにCを埋め込むシステム

1

Agenday 背景：現状のRubyとC拡張ライブラリy 提案：Ricsin: RubyにCを埋め込むシステム
y 従来に⽐べ，書きやすく，低オーバヘッド
y Ricsin の利⽤例と処理の流れ
y Ricsin の記法

y Ricsin の設計と実装

y 評価と考察y 関連研究y まとめと今後の課題
2008/10/28

Ricsin: RubyにCを埋め込むシステム

2

Ricsin記法での記述例
def open_fd(path) # Ruby での記述
fd = __C__(%q{/* C での記述 */return INT2FIX(open(RSTRING_PTR(path), O_RDONLY));})
raise 'open error' if fd == -1
yield fd
ensureraise 'close error' if -1 == __C__(%q{/* C での記述 */return INT2FIX(close(FIX2INT(fd)));})end
2008/10/28

Ricsin: RubyにCを埋め込むシステム

3

背景：Rubyy オブジェクト指向スクリプト⾔語Rubyy 最新版 Ruby処理系 Ruby 1.9.1

y もっとも利⽤されているRuby処理系
y 200x年y⽉リリース予定

y 1.9 からは仮想マシン（VM）を搭載（YARV）
y C⾔語で実装されているので CRuby と表記• Eg. JRuby, IronRuby, Rubinius

y Cで開発されているので，Cで処理を拡張可能
y 組み込みクラスやメソッド

y C拡張ライブラリ（動的リンクライブラリ）
2008/10/28

Ricsin: RubyにCを埋め込むシステム4

背景：RubyからCの機能を利⽤
y Cでないと記述できない処理

y システム依存の処理，レガシーなライブラリの利⽤
y Ruby処理系に踏み込まないと記述できない処理
y Rubyでは性能でない処理

y Cで記述したメソッドを集めたC拡張ライブラリy Cでメソッドを記述 → Cメソッド（vs. Rubyメソッド）
y つまり，Ruby Ù C の遷移は「メソッド」単位
y CメソッドからRubyメソッドを呼ぶことも可能
y Ruby C APIを利⽤して記述

y C だけで考えるとそれなりに書きやすい
2008/10/28

Ricsin: RubyにCを埋め込むシステム

5背景：C拡張ライブラリの作成と利⽤
extconf.rb

Ruby C APIでCメソッドを記述

Makefile

Cファイル
(C)

C ビルド環境(C compiler, etc)

Cファイルで定義したCメソッドを利用するように記述

rbファイル(Ruby)

soファイル(C拡張ライブラ)リ実行に必要なファイル

2008/10/28

Ricsin: RubyにCを埋め込むシステム

読込・実行CRuby
6現状のC拡張ライブラリ作成の問題点
1. 記述性の問題：RubyとCをメソッド単位で分割

CとRubyでファイル分割が必要
メソッドより細かい粒度での処理が記述できない処理が必要な箇所に直接記述できない

yyy•

例外処理やブロック呼び出しなど，Rubyで書いた⽅が圧倒的に簡単な処理もCで書かなければならない

2. 性能の問題：必ずメソッド呼び出しが必要

必ずフレーム⽣成のオーバヘッドが必要
Ruby → C の呼び出し
C → Ruby の呼び出し（VM再帰．こっちの⽅が重い）yyy•
2008/10/28

イテレータを C で実装するとこうなることが多い
Ricsin: RubyにCを埋め込むシステム

7

Ricsin: RubyにCを埋め込むシステム
y 提案：RubyにCを埋め込むシステムRicsin
y 記述性を向上：Rubyプログラム中にCを記述
y RubyにCプログラム⽚を直接埋め込む書式

y Rubyのコンテキスト情報をCプログラム⽚から直接参照

y 性能を改善：VM命令を新設し呼び出しコスト削減
y RubyÙC の遷移をVM命令/関数呼び出しコストでy メソッドの⼀般化が不要で特殊な決めうちが可能

2008/10/28

Ricsin: RubyにCを埋め込むシステム

8

Ricsinの利⽤例
y Cでないと記述できない処理を，Ricsinでは：y システム依存の処理，レガシーなライブラリの利⽤→ 必要な箇所に必要な処理を直接Cで埋め込み
y Ruby処理系に踏み込まないと記述できない処理→ RubyにRuby C APIを直接埋め込み
例：Ruby処理系，Ruby C APIのテスト
y Rubyでは性能でない処理→ Rubyに直接Cを埋め込み，継続的⾼速化
記述性の⾼さ，効率の良さ相互に影響
2008/10/28

Ricsin: RubyにCを埋め込むシステム

9

Ricsinの全体像
rcbファイル（Ruby+C）

Makefile

Ricsin変換器

Cファイル
(C)

C ビルド環境(C compiler, etc)

rbファイル(Ruby)

soファイル(C拡張ライブラ)リ実行に必要なファイル

2008/10/28

Ricsin: RubyにCを埋め込むシステム

読込・実行

Ricsin対応CRuby
10

Ricsin記法
y Ricsin記法：Ruby + C混在プログラム記述記法
y ファイルの拡張⼦はrcb（rb に c を埋め込んだ）
y 完全に有効なRubyプログラムとして設計
y Rubyパーサ，検証器をそのまま利⽤可能
y __C__ などの特定のメソッド呼び出しをC プログラム⽚埋め込み指⽰⼦として利⽤
y ⽂字列リテラルでCプログラムを指定
y 埋め込んだCプログラム⽚は若⼲変換
y 埋め込んだCプログラム中からはRubyの環境がそのまま参照可能
y Ruby C APIを利⽤したCプログラム
2008/10/28

Ricsin: RubyにCを埋め込むシステム

11

Ricsin記法での記述例
def open_fd(path) # Ruby での記述
fd = __C__(%q{/* C での記述 */return INT2FIX(open(RSTRING_PTR(path), O_RDONLY));})
raise 'open error' if fd == -1
yield fd
ensureraise 'close error' if -1 == __C__(%q{/* C での記述 */return INT2FIX(close(FIX2INT(fd)));})end
2008/10/28

Ricsin: RubyにCを埋め込むシステム

12

Ricsinで利⽤するレシーバ
y __C__y Cプログラム⽚をその場に埋め込み

y __Cdecl__

y 関数，マクロ，グローバル変数定義を記述

y __Cinit__

y ロード時に⼀度だけ実⾏する処理を記述

y __Cb__y RubyのブロックをCで記述

y __Ccont__y Cの中にRubyを記述

2008/10/28

Ricsin: RubyにCを埋め込むシステム

13

__Ccont__y Cの中にRubyを埋め込み→ CとRubyを並べるための指⽰⼦ __Ccont__y 便利な省略記法を⽤意 v = true__Ccont__('while (v != Qnil) {') # (a)y ただし，Cの変数
v = nil# (b)__Ccont__(' rb_p(v);')# (c)スコープは切れる__Ccont__('}')# (d)→ Rubyのローカル変数
v = true
で値を共有#C while (v == Qnil) { /* (a) */#C#C }2008/10/28

v = nilrb_p(v);

Ricsin: RubyにCを埋め込むシステム# (b)/* (c) *//* (d) */14

コンテキスト情報へのアクセス
y CからRubyのコンテキスト（変数）情報へ直接アクセス可能y ローカル変数 (lv)y インスタンス変数 (@iv)，グローバル変数 ($gv) 等
y 定数

y self 擬似変数

2008/10/28

Ricsin: RubyにCを埋め込むシステム

15

コンテキスト情報へのアクセス例
def open_fd(path) # Ruby での記述
fd = __C__(%q{/* C での記述 */return INT2FIX(open(RSTRING_PTR(path), O_RDONLY));})
raise 'open error' if fd == -1
yield fd
ensureraise 'close error' if -1 == __C__(%q{/* C での記述 */return INT2FIX(close(FIX2INT(fd)));})end
2008/10/28

Ricsin: RubyにCを埋め込むシステム

16変換と実⾏y rcb -> C+Rubyに変換

y rcbファイルをパースして，埋め込みCを抽出
y 埋め込み式をCの関数（*1）に変換
y 変換プロセスの詳細は省略

y 実⾏時，埋め込み式の実⾏

y 専⽤VM命令 opt_ricsin_callを新設（VM唯⼀の修正点）

y (*1) の関数ポインタを渡すようにバイトコードコンパイル
y Cメソッド呼び出しからVM命令実⾏＋関数呼び出しにy この命令は，VM命令を拡張する命令ということも可能

2008/10/28

Ricsin: RubyにCを埋め込むシステム

17変換と実⾏ (cont.)# rcb ファイル
v = 42r = __C__(%q{/* 埋め込み C ボディ部 */rb_p(self); /* main と表示 */return INT2FIX(FIX2INT(v) + 1);})p r #=> 43 と表示

バイトコードコンパイル[ADDR] [INSN]
0000 putobject
0002 setlocal
0004 opt_call_ricsin
0006 setlocal
0008 putnil
0009 getlocal
0011 send0017 leave
2008/10/28

生成関数呼び出し[OPERAND]42v<funcptr>r/* 生成されるCソースファイルの一部 */#define v (cfp->lfp[3])#define r (cfp->lfp[2])
VALUE ricsin_func_1(rb_control_frame_t *cfp){const VALUE self = cfp->self;{/* 埋め込み C ボディ部 */rb_p(self);
return INT2FIX(FIX2INT(v) + 1);}
return Qnil;}#undef v#undef r

r:p, 1

Ricsin: RubyにCを埋め込むシステム

拡張ライブラリに18

__Ccont__ の変換
v = true#C while (v == Qnil) { /* (a) */v = nil# (b)#rb_p(v);/* (c) */#C }/* (d) */生成

#define v (cfp->lfp[2])
VALUE ricsin_func_1(rb_control_frame_t *cfp){switch (GET_PC()) {case 10: goto label_10;
case 20: goto label_20;}

バイトコードコンパイル[ADDR] [INSN]
0002 putobject
0004 setlocal
0008 opt_call_ricsin
0010 pop
0013 putnil
0014 setlocal
0018 opt_call_ricsin
0020 leave[OPERAND]
truev<funcptr>v<funcptr>{
label_10:;
while (v != Qnil) {; /* (a) */SET_PC(10); return Qnil;
label_20:;
rb_p(v);/* (c) */};/* (d) */SET_PC(25); return Qnil;}}
return Qnil;}2008/10/28

Ricsin: RubyにCを埋め込むシステム

19

⽣成されたC⾔語ファイルのレイアウトy rcb 1 ファイルにつき1つのCファイルを⽣成（詳細は割愛）ヘッダファイル等__Cdecl__で埋め込まれた定義__C__, __Ccont__
で埋め込まれたCプログラム片を変換した関数群
実行するために必要なデータ定義
拡張ライブラリ
初期化関数（__Cinit__ 含む）

2008/10/28

Ricsin: RubyにCを埋め込むシステム

20

評価
y 主に性能に関する評価
y 評価環境y 評価環境1：Intel Xeon E5335, Linuxy 評価環境2：SPARC T2, SunOS 5.10
• 傾向は変わらず．どちらでも実⾏できることを⽰すため．

y 評価項⽬

1. Cの機能呼び出しの実⾏時間⽐較
2. イテレータの⾼速化の例
3. ⾏列計算の⾼速化

2008/10/28

Ricsin: RubyにCを埋め込むシステム

21

評価：Cの機能呼び出しの実⾏時間⽐較
y 空のC機能の呼び出し時間の⽐較
y 空のCメソッドy 空の__C__

評価環境１（Intel）
評価環境２（SPARC）

2008/10/28

C (sec)0.44

Ricsin (sec)
0.05

C/Ricsin
8.8

4.56

0.44

10.4

Ricsin: RubyにCを埋め込むシステム

22

評価：イテレータの⾼速化の例
y イテレータをRicsinで書き直し
y C: 従来

y Ricsin: __Ccont__ を利⽤して Ruby/C をミックス
y Ruby: Rubyで書き直し

評価環境１（Intel）
2008/10/28

評価環境２（SPARC）
Ricsin: RubyにCを埋め込むシステム

23

評価：⾏列計算の⾼速化
y ⾏列の乗算（要素は整数）

y 12⾏のRuby Scriptを36⾏のCプログラム⽚で直接置き換え

評価環境１（Intel）
評価環境２（SPARC）2008/10/28

Ruby (sec) Ricsin (sec) Ruby/Ricsin
10.57
0.57
20.33
85.31

Ricsin: RubyにCを埋め込むシステム

6.73

12.68

24

Ricsinに関する考察
y Cを埋め込むと⾼度な最適化が出来ないのでは？

→ 最適化が進めばそうなるが，その段階は遠いCを対象にしているためポータビリティも⾼い
処理系メンテナンスコストも低い
y 本当に書きやすいの？
→ 数⾏であれば従来のCメソッドより書きやすいエディタ等の対応が必要になるかも

2008/10/28

Ricsin: RubyにCを埋め込むシステム

25

関連研究
y C（とくに gcc）の asm ⽂（C + assembler）
y asm 中に C は記述不可能

y HTML埋め込み⾔語（PHP，JSP，JavaScript）
y Jeannie (OOPSLA 2007)y Java+Cコードを混在

y JavaとJNIコードを⽣成
y Java→C，Java→Cはメソッド呼び出しに変換

2008/10/28

Ricsin: RubyにCを埋め込むシステム

26

関連研究：RubyInline（CメソッドをRubyスクリプトに記述）
読込・実行

CRuby

rbファイル(Ruby+C)

実行時に作成Cメソッド定義をRubyスクリプトに直接記入「ファイルがバラバラ」という問題点だけ解決
2008/10/28

動的読込

Makefile

Cファイル
(C)

C ビルド環境(C compiler, etc)

Ricsin: RubyにCを埋め込むシステム

soファイル(C拡張ライブラ)リ

27

まとめ
y RubyにCを埋め込むRicsinシステムを提案

y メソッド単位ではなく，Rubyに直接Cを埋め込む記法
y コンテキスト情報に直接アクセス可能で記述性向上

y VMに命令を追加することで10倍⾼速なC機能呼び出しy 今後の課題

y C部分を真⾯⽬にパースして連携（の検討）y AOTコンパイラとの連携

2008/10/28

Ricsin: RubyにCを埋め込むシステム

28

おわり
ご清聴ありがとうございました
Ricsin: RubyにCを埋め込むシステム

http://svn.ruby-lang.org/repos/ruby/branches/ricsin/

ささだこういちsasada@ci.i.u-tokyo.ac.jp
ko1@atdot.net, ko1@rvm.jp

謝辞

執筆に協⼒して下さった⽅々
科研費若⼿（スタートアップ）19800007
2008/10/28

Ricsin: RubyにCを埋め込むシステム

29