2015-3-(1): 情報処理学会プログラミング研究会 発表資料 2015 年 11 月 5 日

Ruby 処理系のコンパイル済みコードの設計○笹田 耕一1,a)

松本 行弘1,b)

概要：

Ruby 処理系（Ruby 2.2）は、Ruby プログラムをスタックマシン型仮想機械（VM）の命令列にコンパイ
ルし実行する。実行時にコンパイルするため、変換に時間がかかる、という問題がある。また、複数のプ
ロセスで命令列を共有することが難しいため、マルチプロセス環境で多くのメモリを消費してしまう、という問題がある。これらの問題を解決するため、事前に Ruby プログラムをコンパイル済みコードに変換
し、利用するための仕組みを検討している。本発表では、検討中のコンパイル済みコードの設計について紹介する。

Design of Compiled Code on Ruby Interpreter
Koichi Sasada1,a)Yukihiro Matsumoto1,b)Abstract:
Ruby interpreter (Ruby 2.2) compiles Ruby programs into instrunction sequences for a stack-based virtual
machine. Because compilation is done at runtime, it can be an overhead. It is diﬃcult to share Instruction
sequences with multiple processes, there are memory consumption problem with multi Ruby processes. To
problems these overcome, we are considering about features to compile Ruby program in advance and to use
compiled code. In this presentation, we will show you about current design of compiled code.

1. はじめにRuby 用ライブラリを流通させるパッケージングシステ
ムとして、RubyGems*2 というツールがあり、利用者は容本発表では、Ruby スクリプトを事前にコンパイルする

易にライブラリを用いることができる。RubyGems では、ためのフォーマットと、その実行方式について、現在検討

ライブラリのことを Gem と呼称する。RubyGems によっしている内容を報告する *1 。

て、利用者は容易にライブラリを用いることができ、アプ

本発表は検討中の内容であり、未実装の部分も多いため、結果は予備実験で得られた結果から見積もることで示す。

リケーションや Gem 自体が、多くの Gem を利用して開発が行なわれている。
ウ ェ ブ ア プ リ ケ ー シ ョ ン 開 発 で は 、Ruby on Rails

1.1 背景（RoR）*3 というウェブアプリケーションフレームワー

オブジェクト指向プログラミング言語 Ruby は、その使クを用いた開発が最もよく行なわれている。RoR を用いた

いやすさから世界中で利用されているプログラミング言語

開発をはじめ、Ruby アプリケーションの開発では、多く

で、ウェブアプリケーション開発の分野で、とくに多く利の Gem を利用して開発が行なわれる。

用されている。
1a)b)*1Heroku, Inc.
ko1@heroku.com
matz@heroku.com
本報告の最新版は、http://atdot.net/˜ko1/activities に掲載するので、適宜参照されたい。

1 つの Gem は複数の Ruby スクリプトによって構成されているため、1 つのウェブアプリケーションを実行する
ために、最終的には大量の Ruby スクリプトをロードする*2*3

RubyGems: https://rubygems.org/
Ruby on Rails: http://rubyonrails.org/

1

2015-3-(1): 情報処理学会プログラミング研究会 発表資料 2015 年 11 月 5 日

表 1 簡単なウェブアプリケーションでの利用統計
利用している Gem の数91
読み込んだ Ruby スクリプトの数

1,550

Ruby スクリプトの平均行数

140

Ruby スクリプトの最大行数

2,970

スクリプトのディスクからの読み込みは、計算機が搭載するメモリ容量にもよるが、ディスクキャッシュに載せる、という、OS による古典的な解決策が期待できる。
最後の、パース・コンパイルについては、現状は bison を用いた構文解析と、Ruby 用 VM[3] のバイトコードへの独必要がある。メッセージを保存し表示するだけの簡単な
ウェブアプリケーション *4 を試作したところ、約 100 個の Gem を用いており、アプリケーションのスクリプトとあわせ、合計で 1,550 個の Ruby スクリプトをロードしている（表 1。なお、行数はコメント行や空行なども含む）。
簡単なウェブアプリケーションだけでこれだけのスクリ自に実装した変換器を用いて行なっている。結果をキャッシュすることはないので、Ruby インタプリタを起動する
たびに、すべてのスクリプトについてパースとコンパイルを行なっている。本研究では、コンパイル済みコードを用いることで、この時間の削減を目指す。

1.2.2 利用メモリの増大
次に、メモリ使用量が増えるという問題である。簡単なプトが必要になるため、複雑なものでは、この数倍の Ruby
スクリプトをロードすることになる。

ウェブアプリケーションでも、130MB 程度のメモリを利用するが、その中でも 20MB 程度をスクリプトをコンパイル

1.2 課題このように、Ruby を用いたウェブアプリケーション開発では、多くのスクリプトを用いて実行している。そのことから、起動時間の増大と利用メモリ増が主な課題となる。

1.2.1 起動時間の増大
まず、起動時間の増大が問題となる。プログラムが複雑になり、多くのスクリプトをロードすると、プログラムのした結果が占める。図 1 は、ある時点で確保しているメモ
リ消費量について、どの関数で割り当てられたかで分け、その上位の値と割合を示している *6 。iseq と名前に入っている関数が、コンパイルして得られたプログラムの表現
形式（iseq、Instruction Sequence の略）のために割り当てたメモリ消費量である。以降、この表現形式を慣例に従い
バイトコードと呼ぶ。

ロード時間が長くなる。一度起動してしまえば、その後はプロセス単体で見れば、全メモリ消費 130MB 中、バイプロセスを起動しないため問題は無い、という場合もある

トコード関連が 20MB 消費しており、全体の 15%程度を占が、例えばアプリケーションのテストなどで起動を繰り返めており、あまり大きな割合ではない。
しかし、これが複数プロセス起動した場合、問題になる

すような場合などは、起動時間が大きく作業時間に影響する。
プログラムの読み込みに必要となる実行時間には、いく
つかの要因からなる。

( 1 ) ロードするスクリプトの特定( 2 ) スクリプトのディスクからの読み込み( 3 ) スクリプトのパース・コンパイル
このうち、ロードするスクリプトの特定には、いくつかの解決策が提案され、利用されている。Ruby では、ロードパスと呼ばれる、読み込むスクリプトを探索するディレク

可能性がある。Ruby では、ウェブアプリケーションを並列処理に対応するためには、複数のプロセスを用いる必要がある。fork を用いて、Copy on Write により、書き込みが行なわれない限りメモリを共有することを期待することもできるが、現在のバイトコードのデータ構造は、書き込み領域と読み込み専用の領域を混在して管理しているため、共有ができないこともある。また、fork を用いないで複数プロセスを実行する場合は、同じスクリプトであろう
とも、バイトコードを共有できない。
また、複雑なアプリケーションでは、この 2 倍、3 倍のトリを指定する仕組みがあるが、インストール済みの Gemに比例してロードパスが長くなり、多くのディレクトリエ
ントリを探索しなければならず、遅いという問題があった。
この問題については、Bundler*5 という、実際に利用する Gem を事前に特定しておく仕組みによって、必要以上にロードパスを探索することを抑制し、起動時間を短縮する

数の Ruby スクリプトを読み込むことがある。つまりバ
イトコードが 40MB や 60MB のメモリを占めることになる。このような複雑なアプリケーションを複数のプロセスで起動させると、最悪の場合、バイトコードの領域のために、その領域のサイズにプロセス数をかけたメモリ消費ことができるようになった。また、以前の起動時にロードが必要となる。例えば、8 コアのあるプロセッサを十分にしたスクリプト群のパスを記憶しておき、再起動時にロー利用するために、16 個プロセスを生成した場合、バイト

ドパスを用いずに、記憶したスクリプト群を直接ロードす

コードのために 1 プロセスあたり 40M B 必要だとすると、る、といった工夫も提案されている。
*4*5

ArigatoBook:
ソ
ー
ス
コ
ー
ド
は
https://github.com/ko1/tracer demo rails app、実 行 はhttp://protected-journey-7206.herokuapp.com/ で 確 認 で きる。
Bundler: http://bundler.io/

40M B × 16 = 640M B のメモリがバイトコードのために必要と言うことになる。60M B の場合、同じく 960M B となる。
*6valgrind/massif というツールを利用して調査した。

2

2015-3-(1): 情報処理学会プログラミング研究会 発表資料 2015 年 11 月 5 日DĞŵŽƌǇƵƐĂŐĞ;Ϳ
Ϭй

ϭϬй

ϮϬй

ϯϬй

ϰϬй

ϱϬй

ϲϬй

ϳϬй

ϴϬй

ϵϬй

ϭϬϬй

DĞŵŽƌǇƵƐĂŐĞ;Ϳϭϱ͕ϱϵϱ͕ϳϲϰŝƐĞƋͺƐĞƚƵƉΛĐŽŵƉŝůĞ͘ĐƌďͺŝƐĞƋͺŶĞǁͺǁŝƚŚͺŽƉƚΛŝƐĞƋ͘Đϱ͕Ϯϯϭ͕ϭϯϲŚĞĂƉͺĂƐƐŝŐŶͺƉĂŐĞΛŐĐ͘ĐϰϬ͕ϱϭϴ͕ϰϬϬƐƚͺŝŶŝƚͺƚĂďůĞͺǁŝƚŚͺƐŝǌĞΛƐƚ͘Đϭϴ͕ϵϵϰ͕ϰϴϬƌďͺƐƚƌͺďƵĨͺŶĞǁΛƐƚƌŝŶŐ͘Đϰ͕ϴϭϳ͕ϮϱϮƐƚͺƵƉĚĂƚĞΛƐƚ͘Đϲ͕ϱϳϴ͕ϳϯϲŽŶŝŐͺƌĞŐŝŽŶͺƌĞƐŝǌĞΛƌĞŐĞǆĞĐ͘Đϰ͕ϴϵϭ͕ϵϲϴŽƚŚĞƌƐ

ϯϳ͕ϲϳϲ͕ϴϭϬ

図 1

簡単なウェブアプリケーションでのメモリ消費量（development モード）

ウェブアプリケーション実行プラットフォームとして、

イトコードの形式が異なるため、そのまま利用すること

クラウド上の計算機リソースを使うことが多い。このようはできないが、発想としては同様のものである。ただし、な計算機環境では、メモリなど、計算機資源に応じて利用

mruby は、組込みシステムなど、メモリやメモリ管理が十料金が変更するので、とくに規模の大きなウェブアプリ

分でない環境のために、コンパイル済みコードを長時間保ケーションの運用者にとって、メモリ利用量の削減は重要持しないように、ロード時にコンパイル済みコードからバな課題となる。

イトコードをコピーする。ロード終了後には、コンパイル済みコードの領域をメモリから解放することができる *8 。

1.3 アプローチと既存研究

池原 [5] は、Ruby のバイトコードを圧縮する方法につい本研究では、起動時間とメモリ使用量の増加への対処のて提案している。本研究でも、メモリ消費量を削減するた

ために、Ruby スクリプトをバイトコードへ事前コンパイ

め、同等の手法を用いていることができる。ただし、池原ルを行い、ロードはコンパイル済みコードを読み出すといの手法は、プロセス外に出すことを考慮していないといううアプローチをとる。

点が、本研究の領域とは異なる。

コンパイル済みコードを利用することで、いくらかの前

Ruby の高速化を目的に、Ruby のバイトコードを C 言語

処理は必要であるが、パース・コンパイルの手順を省くこなどを経由して機械語へ変換するというアプローチがいくとが期待できる。また、コンパイル済みコードを、mmap シ

つか提案・実験されている [2], [4]。本研究の目的は Ruby

ステムコールなどで直接ファイルの内容をメモリ上にマップログラムの処理速度の向上ではないが、これらと組み合プし、それをなるべくそのまま利用することで、複数プロ

わせることは十分可能だと思われる。

セスが、そのコンパイル済みコードのメモリを共有可能とできるようにすることを目指す。

2. コンパイル済みコードの検討
コンパイル済みコードに求められる、もしくは望ましい

プログラムを事前に実行形式、もしくは中間形式に出力しておき、実行時に利用する、というのは多くの多くの言特徴を述べる。

語処理系で利用されている。事前にコンパイルしておくこ( 1 ) （必須）ロードして、スクリプトを読み込んだと同様の挙動をすることとから、実行時コンパイル（JIT: Just in Time Compile）と比較して、AoT: Ahead of Time Compile と言うことも( 2 ) スクリプトを変更した場合、コンパイル済みコードを無視、もしくは更新することある。

Java 仮想マシンでは、アーキテクチャの異なるマシン( 3 ) ロードにかかる時間が少ないこと

間で移送可能なクラスファイルという形式を定義してい( 4 ) 他のプロセスと共有できるように、コンパイル済み

る [1]。本研究では、コンパイル済みコードも含め、ほぼコードのデータを直接実行時に利用できること

同様のアイデアを利用するが、マシン間で移送可能とはせ( 5 ) コンパイル済みコードをロードした結果、速度劣化がないことず、ワード長やエンディアンなどは、コンパイルした計算機上のものを利用する。

( 6 ) メモリ消費量を削減するために、サイズが小さいこと

mruby*7 では、すでにバイトコードをファイルから読み込むための仕組みを備えている。mruby と Ruby ではバ*7

mruby: https://github.com/mruby/mruby(1) は、当然のことであるが、スクリプトを読み込ん*8mruby はプログラムにバイトコードを埋め込む機能を有しており、その場合はコンパイル済みコードを解放せずに、そのまま利用する。

3

2015-3-(1): 情報処理学会プログラミング研究会 発表資料 2015 年 11 月 5 日

だ場合と同等の挙動であることは必須である。ただし、__FILE__という、スクリプト名自体をどのようにするか、

3. 実装技術の検討

や、スクリプト自体をファイルとしてアクセスするためのDATA という機能をどのように扱うか、という点は検討を要する。本研究では、これらの点を追求しない。

(2) は、コンパイル済みコードが古くなった場合にどうするべきか、という点である。毎回、ファイルを比べ、タ
イムスタンプを比較する、という手段が直接的な解決策だ
が、そのためには、オリジナルのスクリプトを探索する方法を提供する必要がある。また、ファイルシステムによっては、タイムスタンプを取得するためにオーバヘッドが必要になる可能性がある。本研究では、これらの点も追求しない。

(3) は、ロード時間短縮という目標に直結する。保存されたコンパイル済みコードを実行可能な形に前処理を行なう必要があるが、これをできる限り短時間にすることが望ましい。ロード処理には、例えばインタプリタプロセスに非依存の情報を、プロセスに依存した形に変換する処理がそれにあたる。

(4) は、他のプロセスとメモリを共有することができる
ように、実行時に、なるべく情報をそのまま利用することである。この特徴が促進できれば、(3) のロード時間短縮という目的にも大きく寄与する。例えば、mmap システムコー
ルにより、ファイルをメモリにマップするだけで済めば、本章では、具体的な実装方法について、現在検討している案についてまとめる。

3.1 コンパイル済みコードのデータフォーマット
コンパイル済みコードは、外部記憶に保存するため、特定のインタプリタプロセスに依存しない、独立した形としなければならない。特定のインタプリタプロセスに依存する情報としては、メソッド名などの識別子として利用する

ID （同じ名前について、インタプリタプロセスごとに値が変わる）や、実行時に必要となるオブジェクト（インタプリタプロセスごとに、オブジェクトを生成し、そのオブジェクトへ参照を保持しなければならない）などがある。
このような情報を適切にエンコードしコンパイル済み
コードとして構成し、実行時にはロードするための仕組みが必要である。Ruby には、このような仕組みとして、すでに Marshal という機能があり、異なる Ruby プロセス間でデータを渡す時などに利用する。データフォーマットには、Marshal、もしくは、それに類するもの *9 で生成したバイト列を格納する。
ファイルフォーマットは、このような状況を踏まえ、次のようなヘッダ部を持つ。

struct ibf_header {前処理の時間も少なく、また実際に必要になるまでディス

char magic[4]; /* YARB */クアクセスを遅延することができる。

unsigned int major_version;(5) は、(4) と相反する。実行時にコンパイル済みコード

unsigned int minor_version;

をなるべくそのまま利用しようとすると、その領域からは

unsigned int size;

ポインタでインタプリタ固有の情報にアクセスすることができないため、テーブルなどを用いた間接参照が必要にな

ibf_offset_t iseq_list_offset;

る。とくに、実行に必要となるバイトコード列が間接参照

ibf_offset_t id_list_offset;を行なうと、性能に大きな影響が出る可能性がある。つま

ibf_offset_t object_list_offset;

り、速いバイトコード列を生成するためには、コピーし、イ};ンタプリタプロセス特有の情報を生成する必要があるが、前処理が長くなり、メモリ使用量が増大する、という問題が生じる。

(6) は、ディスクの消費サイズ、およびメモリ消費量の観点から、なるべくコンパイル済みコードのサイズが小さいことが望ましい。しかし、圧縮技術を利用すると、(4) のために、例えばそのまま利用する場合、(5) の実行速度と相反することが多い。ストレージ価格が安くなった現在、ディスクの消費サイズを低くする、という需要はあまりな
いため、本項目の優先度は低い。
このように、考えるべきパラメータがロード時間、メモ

最初の 3 要素は、バイナリフォーマットを識別するためのシグネチャである。size は、このコンパイル済みコードの長さである。

iseq_list_offset は、格納されているバイトコードの数だけエントリを持つテーブルへのオフセットであり、各エントリは、バイトコードの情報の先頭が、コンパイル済
みコードのオフセットを示す。
わかりづらいので、C 言語のソースコードで示すことにする。3 番目のバイトコードの情報が欲しいときには、次のようなプログラムによって得ることができる。

char *code = コンパイル済みコードバイナリ列;

リ消費量、処理速度があり、主に処理速度とロード時間、メモリ消費量が相反するというトレードオフとなる。

*9

バイトコードが含む一部のオブジェクトが、Marshal では対応できないことがわかっている。また、マシンや Ruby のバージョンに非依存の形で出力するため、いくらか効率が悪い。そのため、これらの問題を解決する別の実装が必要と考えている。

42015-3-(1): 情報処理学会プログラミング研究会 発表資料 2015 年 11 月 5 日

䝞䜲䝖䝁䞊䝗ϭ䝞䜲䝖䝁䞊䝗ϭ䝞䜲䝖䝁䞊䝗Ϯ䝞䜲䝖䝁䞊䝗ϰ䝞䜲䝖䝁䞊䝗ϯ䝞䜲䝖䝁䞊䝗ϱ

䝁䞁䝟䜲䝹
῭䜏䝁䞊䝗

䝞䜲䝖䝁䞊䝗Ϯ䠄ᮍ䝻䞊䝗䠅

䝞䜲䝖䝁䞊䝗ϯ䠄ᮍ䝻䞊䝗䠅

図 3 バイトコードを遅延ロードした場合ドオブジェクトとして生成する。
図 2

バイトコードを全てロードした場合この方法では、バイトコードの実行や、情報の参照のたびに、未初期化かどうかのチェックを行なう必要があるた

struct ibf_header header = (HEADER *)&code[0];め、実行時オーバヘッドが生じる。このオーバヘッドを避

ibf_offset_t *offsets =けるため、未ロードのバイトコードを実行すると、「ローcode[hader->iseq_list_offset];
bytecode_header *bheadr =ode[offsets[3]]; /* 3 番目 */0 番目のバイトコードがトップレベルのバイトコードであり、これを起点にバイトコードをロードしていく。

ドして再実行する」という処理にしておくことで、ロード済みのバイトコードを実行するたびに、毎回ロード済みかチェックする、ということは避けられる。

4. 実験と効果の見積もり

id_list_offset と object_list_offset も 、同 様 に本報告の執筆時点では、まだ実装が完了していないため、

ID、オブジェクトを示すバイト列（Marshal などで生成）実装が完了したいくつかの機能をもとに実験を行ない、コへのオフセットのテーブルである。

ンパイル済みコードによる効果を見積もることとする。

この形式への変換、およびこの形式からのバイトコードの復元は、ほぼ自明であるため、ここでは詳述しない。

なお、評価は Intel i5-3380M（2.90GHz）CPU で動作する

Windows 7 上で動作する VirtualBox 上で動作する Ubuntu
14.04.2 LTS で行い、対象とする Ruby は 2015-11-01 trunk

3.2 バイトコードの遅延ロード

52420 を用いている。

RubyVM のバイトコードは、スコープごとに別々のバ
イトコードに別れており、親のバイトコードを実行するために必要となるバイトコードを、子として親が参照する、という木構造となっている（図 2）。

4.1 ロード時間の見積もり
現状、コンパイル済みコードを読み、内容をコピーし前処理することで、バイトコードを復元する版がほぼ完成

Ruby のプログラムは、例外処理や、使われないメソッ

している。この版では、コンパイル済みコードを複数プロ

ドといった、一度も実行されないようなバイトコードが存

セスで共有する、などといったことができない。また、す在し、これらを毎回ロードするのは無駄である。そこで、

べての Ruby スクリプトに対応していないため、実アプリ必要になるまでロードを遅延するようにすることが考えら

ケーションでの評価は行えない。

れる（図 3）。
図 3 では、バイトコード 1 がすでにロードされている状況を示している。バイトコード 1 の実行に必要なバイト

今回は、この版を用いて、通常通りパース・コンパイルを行なう場合と、コンパイル済みコードを読み込む場合の時間の違いについて調査する。

コード 2、および 3 は、まだロードしていない、という状態

まず、計測対象となる約 1,400 行の Ruby スクリプトを用のみで表現（具体的にはバイトコードに対応するオブジェ意した。スクリプトでは、100 個のクラス定義が記述してあクトを生成）しており、詳細はコンパイル済みコードの内り、各クラスに単純な 3 つのメソッドが定義している。合計容を確認すれば手に入るようになっている。バイトコード

で 1(toplevel)+100(classdef inition)+100∗3(methods) =2 の実行に必要となるバイトコード 4 および 5 の表現はイ

401 個のバイトコードが生成される。

ンタプリタ内には存在しない。

このスクリプトを対象に、(1) ファイルから読みこんだ

実際に、バイトコード 1 の処理が進み、バイトコード 2

状態からパース・コンパイルし、バイトコードを生成するが実行される段階になって、その内容をロードする。この(2) コンパイル済みコードをファイルから読み込んだ状態

とき、バイトコード 4 および 5 を、未ロードのバイトコー

から、ロードする、(3) 遅延ロードを利用する、という 3

5

2015-3-(1): 情報処理学会プログラミング研究会 発表資料 2015 年 11 月 5 日

ないことがわかった。つまり、残りの 85% のバイトコー

表 2 ロード時間の実験結果(1-1) パース・コンパイル

7.05 sec(1-2) (1-1) に加え、クラス定義を実行

8.42 sec(1-3) (1-2) - (1-1)

1.37 sec(2-1) コンパイル済みコードのロード

2.22 sec

ドは実際には利用されないということであり、遅延ロードに大きな効果が期待できることがわかった。
これは、エラーなどが起こらない、正常系のみ実行した(2-2) (2-1) に加え、クラス定義を実行

3.41 sec

ためだからだと思われる。また、ライブラリのメソッドな(2-3) (2-2) - (2-1)

1.19 sec

どは、一般的に全てを利用しない。例えば、ファイル操作(3-1) 遅延ロード

0.00 secを行なう FileUtils というライブラリには、50 のメソッ

(3-2) (3-1) に加え、クラス定義を実行

2.06 sec

ドが含まれるが、その中の 1 つを利用したい場合でも、こ(3-3) (3-2) - (3-1)

2.06 secのライブラリをロードする。この場合、その他の 49 個のメソッドは未使用ということになる。

つについて、それぞれ 2,000 回繰り返した時間を計測した。

前 節 で 求 め た 1 フ ァ イ ル あ た り の ロ ー ド 時 間 1.1ms

どちらもファイルからスクリプト、もしくはコンパイル済を 表 1 の 1,550 個 の フ ァ イ ル に あ て は め て み る と 、みコードを読み込んだ状態で行なっているので、ファイル

1.1ms × 1, 550 = 1, 705ms であり、ロード時間が遅延システムの影響は排除している。

ロードによってその 15%となるとすると、255ms 程度にな

実験の結果を表 2 に示す。(1-1) が 7.05 秒 (2-1) が 2.22秒となり、パース・コンパイルすることに比べ、ロードす
るだけのほうが 3 倍程度速い、ということがわかった。まると予想できる *10 。

5. おわりにた、遅延ロードを用いることにより、実際にはバイトコー

本発表では、Ruby にコンパイル済みコードの生成、おドのロードは 1(toplevel) + 100(classdef inition) = 101 回

よびロードする機能を設けることで、ロード時間を短縮し、しか行なわれないため、読み込み・実行までの時間で比較

メモリ削減を目指す方式について検討し、いくつかの予備すると、(1) と比べ、約 4 倍、(2) に比べ約 1.7 倍速いこと

評価を行なった。

がわかった。

実際のアプリケーションの挙動を観察すると、遅延ロー実験に利用した Ruby スクリプトは、100 個のクラス定ドにより、85%のバイトコードの生成を抑えられる可能性義があるという点で、実際よりも複雑であるが、大ざっぱが確認できた。バイトコード表現を工夫し、コンパイル済に見積もると、(1) は 1 つのファイルのロードに 3.5ms、(2)みコードとデータを共有するようにすることでロード時間

では、1.1ms かかることがわかった。

を削減し、複数プロセスでのメモリ共有を促進することが今回は、コンパイル済みコードとデータを一切共有せず、できるが、一般に共有によって間接参照が増えてしまい、

ロード時にすべてコピーし、必要であれば改変する方式をバイトコード実行速度が遅くなるという問題を生じる可能とった。これをコンパイル済みコードとデータを共有する性がある。そのため、このような工夫はしないでも十分か方式になれば、より高速にロードできるようになることがもしれない。今後、より多くの評価を行なって、この結果期待できる。

を検証していきたい。

実行時間は、どれもまったく同じバイトコード表現を生成するため、差異はないと判断できる。ただし、コンパイ

参考文献

ル済みコードとデータを共有する方式を検討していくと、[1]

間接参照などが増える可能性があり、速度低下の要因となる可能性がある。

[2]

なお、用意した Ruby スクリプトのサイズは 19,050 バイ
トであり、変換したコンパイル済みコードは 237,536 バイ[3]トであった。64bit CPU を利用しており、そのワード幅をそのまま用いているため、サイズが大きくなっている。適[4]

切な圧縮方法を用いれば、容易に小さくすることができる
が、ロード時間とのトレードオフとなる。

[5]

4.2 遅延ロードの見積もり

ティム・リンドホルム，フランク・イェリン：Java 仮想マ
シン仕様第 2 版，ピアソン・エデュケーション (2001).
井出真広，倉光君郎：Ruby 向けトレース方式 Just-in-time
コンパイラの設計と実装，情報処理学会論文誌プログラミ
ング（PRO），Vol. 8, No. 1, pp. 1–10 (2015).
笹田耕一，松本行弘，前田敦司，並木美太郎：Ruby 用仮想マシン YARV の実装と評価，情報処理学会論文誌 (PRO)，
Vol. 47, No. SIG 2(PRO28), pp. 57–73 (2006).
芝哲史，笹田耕一， 平木敬：CastOﬀ: Ruby 用コンパ
イラのライブラリとしての実装，情報処理学会論文誌プロ
グラミング（PRO），Vol. 5, No. 3, pp. 1–22 (2012).
池 原 潔：組 込 み シ ス テ ム の た め の 動 的 コ ン
ポ ー ネ ン ト 機 構 と VM の 最 適 化.
RubyKaigi2011
http://rubykaigi.org/2011/ja/schedule/details/16S03.

簡単なウェブアプリケーションに対して、起動してから10 回接続した後で、生成されたバイトコードの数と、実際に実行された数を確認したところ、それぞれ 30,485 個と4,698 個であり、およそ 15%のバイトコードしか実行され*10

ただし、前節でも述べたが、実験で利用したスクリプトは複雑な Ruby プログラムであるため、1.1ms という結果は、実際にはもっと短くなる可能性がある。

6