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

Ruby 3 に向けた新しい並行実行モデルの提案○笹田 耕一1,a)

松本 行弘1,b)

概要：
マルチスレッドプログラミングは、共有するデータに対して、複数のスレッドから同時に読み込み、書き込みを行なうとデータレースなどの問題を発生させるため、排他制御などの同期を適切に行なわねばならず、難しい。この問題は、スレッドが変更可能なデータを簡単に共有できるため生じるので、そのような共有を制限する方法が必要である。
プログラミング言語 Ruby の次期メジャーバージョンである Ruby 3 では、マルチスレッドプログラミン
グのもつ難しさを緩和するため、“Guild” という Ruby 向けの新しい並行実行モデルを検討している。インタプリタは 1 つ以上の Guild をもつ。変更可能なすべてのオブジェクトはある 1 つの Guild に所属し、Guild 間では、変更可能なオブジェクトを共有しない。ただし、不変オブジェクトや、クラスやモジュールは共有する。Guild は 1 つ以上のスレッドをもち、異なる Guild は並列に実行する。Guild 間の通信はチャンネルを通して行ない、変更可能なオブジェクトを Guild 間で通信するにはコピーと移籍という二つの方式を提供する。移籍は、所属する Guild を変更する操作であり、単なるコピーより軽量となる。性能などの観点から、変更可能なオブジェクトを共有しなければない場合、特別なデータ構造を提供することができる。
本提案は、既存のアイデアの「いいとこ取り」を狙ったものである。本発表では、この並行実行モデルの目論見について議論する。

A proposal for a new concurrent execution model in Ruby 3
Koichi Sasada1,a)Yukihiro Matsumoto1,b)Abstract:
Multi-thread programming is diﬃcult because data which shared by multiple threads requires appropriate
synchronization by mutual exclusion or other techniques to avoid issues such as data race and so on. This
problem is because sharing mutable data between threads easily, so that we need a way to restrict such
sharing.
Ruby 3 is the next major release of programming language Ruby. Ruby 3 aims to introduce new concurrent
execution model for Ruby named “Guild” to avoid multi-thread programming diﬃculties. An interpreter has
at least one Guild. All of mutable objects belong to one guild and multiple guilds can not share mutableobjects. But guilds can share immutalbe objects and class/module objects. Guild has at least one thread andthreads belong to diﬀerent guild can run in parallel (simultaneously). Inter guild communication is given bychannel. There are two way to communicate mutable objects each other, one is copy and another is transfer
membership (shortly, move). Transfering membership means changing belonging guild to another guild. It isfaster than simple copying. If we need to share mutable objects because of performance, we can oﬀer special
data structure.
Our proposal is to cherry picking existing good ideas. In this presentation, we will discuss about our concurrent execution model design.

1

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

1. はじめに
プログラミング言語 Ruby は世界中で広く利用されておドのみが Ruby プログラムを進めることができ、コンテキ
ストスイッチのタイミングで解放し、他のスレッドが獲得し、そのスレッドが実行を継続する。

り、とくにウェブアプリケーション開発で活用されている。

同時並列なスレッドの動作を許さない理由は、主には同

ウェブアプリケーションは大規模なものもあり、インタプ

時並列に動作するプログラムを適切に記述するのが困難だ

リタの性能が重要となっている。

からである。すでにスレッドによる並行プログラミングを

我々は、Ruby を実行する Ruby 処理系（以下、CRuby）

サポートしているため、本質的に同種の難しさを持ってはを開発しており、最新版は Ruby 2.3.1 であり、2016 年末

いるが、しかしプリエンプションのタイミングをメソッドに Ruby 2.4.0 のリリースを予定している。現在、Ruby のの終了時などに限定しているため、程度問題としてだが、次のメジャーバージョンである Ruby 3 の仕様について

完全に同時並列にスレッドが動作するよりも簡単に Ruby

議論しており、静的解析による実行前のエラー検出、JIT

によるスレッドプログラミングが可能である。

コンパイルなどによる高速化、よりよい並行並列処理のサ
ポートの三つを大きな目標としている。

また、インタプリタ開発も、並列プログラミングに特有の、メモリオーダリングの考慮といった難しさから解放さ

本発表は、Ruby 3 の目標の一つである、並行並列処理のれ、格段に容易となる。とくに、CRuby は予期しない終了

サポートのために検討している Guild という新しい並行・（例えば、セグメンテーションフォールトによる強制終了）

並列プログラミングのための機構について報告する。

をできる限り許さないという方針を立てている。これを実

2. 背景と課題
2.1 Ruby 2 での並行並列処理のサポート

現するためには、排他制御など細粒度同期処理を適切に配置しなければならないが、これは困難である。また、必要となる細粒度同期処理のオーバヘッドを低減する必要があ

Ruby は当初よりスレッドによる並行処理をサポートしり、様々な提案が行なわれている。これらは、手数があれている。いわゆる「スレッド」と言われたとき、多くの人

ば（おそらくは）解決可能な問題だが、CRuby 開発にかけが期待するものと同様である。

られる工数には限りがあるため、現実的ではない。

CRuby が起動すると、最初に 1 つのスレッドを起動し、このように、Ruby プログラマおよび Ruby インタプリ

そのスレッドは他のスレッドを生成することができる。複タ開発者に優しい設計となっており、並列処理によるプロ

数のスレッドがある場合、プリエンプションや入出力など

グラムの性能改善を行ないたい者にとっては冷たい設計とによる処理のブロック時にコンテキストスイッチが行なわなっている。

れるため、プログラマはスレッド切り替えのタイミングに

なお、C で記述したプログラムを、スレッドセーフであ

ついて意識する必要なく、並行プログラムを Ruby で記述ると宣言し、並列に実行する仕組みは存在し、たとえば入することができる。

出力処理や大きな多倍長整数の演算、zlib などの圧縮、展

すべてのスレッドはすべてのオブジェクトを共有するた開処理などは並列に実行される。

め、スレッド間の通信は、グローバル変数を利用する、共JRuby や Rubinius といった、CRuby 以外の Ruby 処理有する環境のローカル変数を経由する、といった様々な方

系では、スレッドの並列実行を許しているが、Ruby プログ法で行なうことができる。複数のスレッドから、同じオブラマが適切な同期を行なうことを期待している。例えば、ジェクトを参照することができるため、可変オブジェクト

複数のスレッドが同時に Ruby オブジェクトに読み書きしは、その読み書き時に適切に排他制御などの同期を行なう

ようとすると、処理系が異常終了する、といったことが起必要がある。同期のための仕組みとして、Mutex や Queue

こる。また、JIT コンパイルとメモリオーダリングの問題などがある。

から、逐次実行していれば起こりえないような問題が発生

ここからは、実装固有の事情となる。CRuby では、スレッドの実現に OS などが提供するネイティブスレッド

しており、現在、Java のようにメモリモデルを策定しようという提案が行なわれている *1 。

（例えば、POSIX 環境では Pthread）を利用している。同時実行可能なプロセッサコアが複数ある場合（そして、現

2.2 課題状そうではない計算機を探す方が困難であろう）でも、ス

Ruby 3 では、主に (1) 並列処理のサポート (2) より使

レッドは同時に Ruby プログラムを実行することはない。

いやすい並行・並列プログラミングのサポートを実現した

インタプリタが唯一持つ GVL（Global VM Lock、Python

いと考えている。

などでは GIL: Global Interpreter Lock）を獲得したスレッ

なお、実現においては、過去の Ruby プログラムとの互換性を保つことが求められる。これは、既存の Ruby プロ

1
a)
b)

Heroku, Inc.
ko1@heroku.com
matz@heroku.com*1[Feature #12020] Documenting Ruby memory model¡https://bugs.ruby-lang.org/issues/12020¿2

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

グラム、および Ruby 用に記述された C 言語による拡張モに強制する。静的型システムがある言語では、複数の並行

ジュール（C 拡張）が、Ruby 3 でも問題無く動くことを実行単位が同時にアクセスするようなことがないよう、型意味する。

システムを構築するものもあり、これらはすべて実行前に

2.2.1 並列処理のサポート解決される。

現在の MRI では、Ruby で並列処理を記述するために

これらのアプローチを Ruby に適用可能か検討する。

は、複数プロセスを用いるしか方法がない。しかし、複数

Ruby は変更可能なオブジェクトを操作しながら進めるププロセスを利用すると、メモリ消費が増大する、プロセス

ログラミング言語なので、変更を禁止するのは現実的では間通信により、プログラミングが面倒になる、といった問ない。同様に、Clojure のような、変更処理に特殊な制限題がある。スレッドを並列実行する、という、JRuby なを入れることも難しい。また、Ruby には型を明示的に示どと同様の解決策もあるが、処理系開発に大きな負担がか

す方法が無く、Ruby 3 でも導入する予定はないので、こかる。

れも困難である。

2.2.2 より使いやすい並行・並列プログラミング
スレッドプログラミングは難しい。複数のスレッドが同時に一つのオブジェクトへ読み書きを行なうために生じるデータレースやレースコンディションはよく知られていこのように、スレッドをベースに拡張するのは、これ以上困難であるため、スレッド以外の仕組みを提供する必要がある。
解決したい課題を以下のように整理する。

る。これらの問題はタイミングによって非決定的に生じる( 1 ) Ruby 2 で動作するプログラムがそのまま動作する。

ため、再現することが難しく、発生箇所の特定も困難で、デ( 2 ) スレッドとは異なる（もしくは、拡張した）並行実行

バッグが難しい。また、処理順序によってはデッドロック

単位を提供する。

やライブロックを引き起こす（ただし、本報告では、デッ( 3 ) 並行実行単位は、可能なら同時並列に動作する。

ドロック・ライブロックの排除については議論しない。と( 4 ) 同一のオブジェクトに対して同時に読み書きを行なわいうのも、この問題は、問題の発現時の状況把握がある程ないように制限することで、同期について気にせずプ度可能であり、解決が他の問題よりも容易と考えられるか
らである）。
このような課題に対し、スレッドプログラミングを容易にするため、いろいろな手法が提案されてきた。(1) より使いやすい排他制御、同期機構の提案、並列処理に適したデータ構造の提案、(2) スレッドデバッグ手法の提案などである。

ログラムを行なうことができる。

( 5 ) 並行実行単位間での通信は高速に行なうことができる。
( 6 ) 処理系開発に大きな負担はかからず、維持が容易である。

Ruby 3 では、これらの課題の解決を目標としている。

3. Guild による並行・並列制御

しかし、(1) の提案は適切に利用することが求められ、不Ruby 3 にて、並行・並列実行をサポートする機構とし用意に用いると意図しない結果が引き起こされるという点て、スレッドを拡張する Guild を提案する。Guild を用いは変わらない。また、プログラムが煩雑になる。例えば、ることで、スレッドプログラミング特有の難しさを排除し、Java ではスレッドセーフなデータ構造と、そうでないデー

Ruby による並列プログラミングを実現する。

タ構造を適切に選択する必要があるが、その選択を適切に行なうのは難しい作業である。(2) はツールなどの不完全さから完全に危険を除去するのは困難であり、また実装も難しい。

3.1 概要Guild はスレッドの上位に存在する概念となる。プログ
ラムは最低 1 つの Guild を持ち、Guild には 1 つ以上のス

プログラミング言語レベルで並行・並列処理に対応しよ

レッドが所属する。ある Guild に所属するスレッドは、こうとするアプローチもある。排他制御などを不要にするたれまでと同様に並行に実行されるが、並列には実行しない。

め、スレッドなどの並行実行単位が、同一オブジェクトに通常の書き換え可能なオブジェクト（以下、可変オブ

対して同時に読み書きしないように制限するのがよくあるジェクト）は、すべて、ある一つの Guild に所属する。所

手法でる。

属する Guild 以外のスレッドからは、そのオブジェクトへ

例えば、プログラミング言語 Erlang では、データ型を読アクセスすることはできない。そのため、他の Guild に所

み込み専用にし、変更不可にすることで複数の並行実行単属するスレッド同士は、一つの可変オブジェクトに対して位（Erlang ではプロセス）がデータレースなどを起こさな

同時に読み書きを行なうことはない。そのため、所属する

いようになっている。プログラミング言語 Clojure では、Guild が異なるスレッドは、同時並列に実行することがで基本的には変更不可であるが、変更可能なオブジェクトのきる。現在の GVL を、Guild ごとに持つようなイメージ

読み書きを、排他制御を強制するデータ構造（具体的にはとなる。

ソフトウェアトランザクショナルメモリなど）を使うよう

また、書き込みが出来ないように制限した不変オブジェク

3

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

トは、同時に読み書きを行なわれる恐れがないため、 Guild

う性質を維持するため、オブジェクトの参照を制限する必間で共有可能である。

要がある。そこで、特別な通信路を用意し、これのみで通いくつかの特殊なオブジェクトは、書き換え可能でありながら、 Guild 間で共有できる。それらのオブジェクトへ信するようにする。

3.3.1 チャンネルを用いた通信のアクセスは、同期を行なうために十分に制限される。こGuild 間 の 通 信 は 、チ ャ ン ネ ル に よ っ て 行 い 、れらのオブジェクトを特殊共有オブジェクトと呼ぶ。クラ

Guild::Channel として提供される。チャンネルは、ススやモジュール、Guild 間の通信に利用するオブジェクト

レッドセーフな FIFO キューとして実装され、オブジェク

や、Guild を表現する Guild オブジェクト自身がこれにあトの参照を Guild を跨いだスレッド間で送り合うことがで

たる。

きる。

まとめると、Guild においては、すべてのオブジェクト

オブジェクトの転送は、不変オブジェクト、および特殊は次の 3 通りに分類される。

共有オブジェクトについては問題無いが、可変オブジェク

可変オブジェクト Guild 間で共有不可

トは Guild 間で共有を許可しないため、そのまま転送でき

不変オブジェクト Guild 間で共有可能ない。そこで、(1) コピーと (2) 移籍という、2 種類の方法

特殊共有オブジェクト Guild 間で共有可能を用意する。

可変オブジェクトは Guild 間で共有しない、という制限

転送時におけるコピーは直感的に理解可能な方法であにより、多くのオブジェクトへのアクセスについて、同期る。転送されるオブジェクトへの参照は、コピーされたも処理に関する考慮を不要とする。インタプリタ性能を落とのであるため、複数の Guild 間で共有されることはない。

すこと無く、また維持も容易になる。

コピーするのはそのオブジェクトだけではなく、そのオブジェクトから辿ることができるすべての可変オブジェクト

3.2 不変オブジェクト
不変オブジェクトは、そのオブジェクトに対して書き込みができないため、Guild 間で共有可能である。

に対してコピーを行なう。コピーに際し、適切に Copy on

Write の仕組みを利用することで、コピーのオーバヘッドを削減できる可能性がある。

不変オブジェクトとは、そのオブジェクトへの書き込み

移籍は、所属する Guild を変更する操作である。送りができず、またそのオブジェクトから辿って参照可能なす

元 Guild からはアクセスできなくし、送り先でのみアクセ

べてのオブジェクトが不変オブジェクト、もしくは特殊共ス可能とする。コピーよりも高速に転送することが可能で有オブジェクトである必要がある。

ある。コピーと同様に、再帰的に移籍操作を行なう。送りRuby には freeze というメソッドがあり、これが呼ば

元 Guild にて、転送済みオブジェクトへアクセスすると、れたオブジェクトは書き換え不可となるが、そのオブジェ

例外が発生する。例外により、プログラマは転送済みオブ

クトから参照されるオブジェクトが書き換え可能である場

ジェクトへアクセスしたことを確認することができる。よ合があるため、単に freeze されたオブジェクトは不変オ

り詳細に述べると、移籍は、送り元の所属 Guild から離籍

ブジェクトとはならない。例えば、a1 = [1, 2].freeze

し、送り先に加入する、という操作になる。

として生成した配列オブジェクト a1 は、書き換えができ

なお、移籍により転送したオブジェクトの一意性は保証しず、参照しているオブジェクトが不変オブジェクトであるない。つまり、各オブジェクトに一意に振られる object_id

整数値であるため、不変オブジェクトと言える。しかし、が、移籍前と移籍後に異なる可能性がある。

a2 = [[3], 4].freeze として生成される配列オブジェク

移籍の実現手法については後述する。

ト a2 は、書き換え不可だが、最初の要素が書き換え可能な配列であるため、不変オブジェクトではない。

3.3.2 その他の通信
Guild 間で配列や連想配列を共有するのが自然な場合が数値やシンボルオブジェクトは、他の参照を持たず、生ある。現在、具体的にどの程度提供するかは決めていない

成時から freeze として生成されているため、典型的な不が、特殊共有オブジェクトとして提供することは可能であ

変オブジェクトということができる。文字列は他の参照をる。それらのオブジェクトへの読み書きは、特定のプロト

持たないことが多いが、書き換え可能（freeze ではない）

コルを前提とすることで、適切な同期や排他制御行を強制ため、不変オブジェクトではない。不変オブジェクトとすすることができる。例えば、ソフトウェアトランザクショ

るためには明示的な freeze が必要である。

ナルメモリ的なデータ構造を提供したり、内部的にはロッ不変オブジェクトを他の Guild に渡す場合、参照を渡すのみで良いため軽量である。

クフリーデータ構造を用いたコンテナデータ構造を提供する、といったことが考えられる。
また、外部のトランザクション処理の可能なデータベー

3.3 Guild 間の通信
可変オブジェクトがただ一つの Guild に所属する、といス（典型的には RDB）を用いて、Guild 間データを共有する、といったことも可能である。

4

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

3.4 言語仕様の拡張

べてのスレッドを止め、一斉にマークを行なうよう検討し

可変オブジェクトは単一の Guild に所属する、という条ている。スイープ処理は並列に行なっても良いような実装

件を満たすために、Ruby の言語仕様を拡張する必要があとなっている。[5] で提案した手法がそのまま利用できるる。まだ決定は行なわれていないため、現在必要だろうと見通しである。

検討している変更点について述べる。

カレントワーキングディレクトリは、プロセスグローバ

まず、グローバル変数は一つの Guild ローカルな変数と

ルな 1 つを用いるか、スレッドごとに異なるワーキングする。プロセスでグローバルな変数は導入しない。これにディレクトリを用意するか、検討を要する。現状はプロセ

より、$i+=1 といった、単純なカウンタ操作において、同スグローバルであるため、互換性について留意する必要が期を行なわずに値がずれる、といったバグを排除できる。

ある。

ただし、$LOAD_PATH といった、特殊な設定のために利用されるグローバル変数については、判断を保留する。

クラスやモジュールは特殊共有可能変数であるため、メ
ソッドテーブルなど、これらが管理するデータは同期処理

クラスやモジュールは Guild 間で共有する。定数の定義が必要になる。そのため、テーブルアクセス時にはロック

はクラスやモジュールに対する書き込みであるため、Guildなどを導入する。同様に、シンボルテーブルなどの、イン間で共有されることになる。また、クラス変数や、クラス

タプリタでグローバルに管理しなければならないテーブル

やモジュールを含む特殊共有オブジェクトのインスタンス

も、同期処理を挿入する必要がある。

変数も、Guild 間で共有される変数となる。これらについ

その他、内部的に Guild 間で同時に読み書きされるデーては、(1) 共有可能なオブジェクトのみ、代入を許可する

タについても、すべて適切な同期処理を挟む必要がある。

(2) 代入した Guild でのみ、アクセスを許可し、その他の

例えば、インラインメソッドキャッシュがそれにあたるが、Guild からのアクセスを禁止する、(3) メイン Guild（起動

これも [5] で提案したように、更新時は新しいエントリを時に生成された Guild）からのみアクセスが可能、という作るようにすると良いと思われる。

3 つの方法が考えられる。互換性を考えると、(2) もしくは (3) が候補となるが、今後検討していく。

C 拡張には、並列に動作することができない、マルチス
レッドセーフでないものが含まれていることが当然考えら

クロージャを表現する Proc オブジェクトや、ローカルれる。そこで、既存の C 拡張はメイン Guild 以外では動変数などの環境を表現する Binding オブジェクトをどの作しないようにし、マルチスレッドセーフであると宣言しように扱うかも問題になる。転送時にコピーを行なうのがた C 拡張のみ、その他の Guild で実行できるとする。これ自然であるが、例えば、下記のように生成した Proc オブは、[6] で提案した手法である。

ジェクトについて考える。

各オブジェクトには、freeze されたオブジェクトであa = 1

るかどうかを示すフラグがあるが、参照しているオブジェ

pr = Proc.new{ a = 2 }クトが不変オブジェクト（もしくは特殊共有オブジェクト）

ここで pr を他の Guild に転送し、実行しても、転送元のローカル変数 a には変更がないことになる。このようなである、不変オブジェクトを示すフラグがないため、これを付与する必要がある。

挙動が、許容されるかは、これから議論が必要である。なお、移籍はより困難であると思われる。環境への書き込みが無い場合、これらのデータを不変データとして扱う方法を提供するのも 1 案である。

4. 実装

4.2 Guild 間のオブジェクトの移籍
移籍は、オブジェクトの送り元からの離籍と、送り先への加入、という 2 つの操作によって行なわれる。
離籍は、次の 3 つのステップで行なわれる。(1) 離籍時に新規オブジェクトヘッダを生成し、(2) ヘッダの中身だ

Guild の実装について紹介する。実際に実装したわけでけをコピーして、(3) 元オブジェクトをアクセス不能オブ

はなく、まだ構想段階であるため、不備が残っている可能

ジェクトにする。つまり、離籍時には新規オブジェクトを性が十分にある。

生成するが、ヘッダの確保、および設定のみで済むため、単なるコピーよりも高速である。オブジェクトヘッダを新規

4.1 インタプリタの並行制御
インタプリタで唯一管理していた GVL を、各 Guild が生成するため、転送前と後のオブジェクトのアイデンティ
ティ（object_id）の同一性は保証しない。

管理するようにする。これにより、異なる Guild に所属(3) のアクセス不能オブジェクトへの変換は、クラスをするスレッド同士では、同時並列に動作することが可能に差し替えることで実現可能である。つまり、転送後、転送なる。

元でどのようなメソッドを呼ぼうとも、例外などが発生すマークアンドスイープにより実装されているガーベージ

るように差し替える。

コレクション（GC）は並列制御の対象となる。現在は、す52016-3-(5): 情報処理学会プログラミング研究会 発表資料 2016 年 10 月 26 日を利用した場合、Multi/copy はコピーを行なった場合と実行時間（秒）

Single-Guild

なっている。

19.45

表 2 の (A) を見ると解るとおり、Multi/ref では、単一Multi-Guild
10.45
表 1 フィボナッチ数を求めるアプリケーションの実行時間

Guild を用いる場合に比べて 1.6 倍ほど高速となっており、（2 仮想ハードウェアスレッド上）

並列実行の効果を確認できるが、Multi/move、Multi/copy(A) 実行時間（秒） (B) 実行時間/opt（秒）Single-Guild

1

1

Multi/ref

0.64

0.64

Multi/move

4.29

0.64

Multi/copy
5.16
N/A
表 2 配列の要素を足し合わせるアプリケーション（2 仮想ハードウェアスレッド上）は 4 倍、5 倍程度、逆に遅くなっている。これは、10 万要
素の配列について、前者は移籍のため、後者はコピーのために処理が入るため、そのオーバヘッドにより遅くなっている。移籍の場合、すべての要素について、共有可能なオ
ブジェクトであるかどうかをチェックするため、その走査のために遅くなっている。ただし、Multi/move のほうがMulti/copy に比べ 1.2 倍ほど高速である。
何らかの最適化が必要であるが、ここで配列のすべての

5. 予備評価

要素が共有可能オブジェクトであることを既知と仮定して移籍を行なった結果が表 2 の (B) である。この結果では、

まだ実装を行なっていないが、チャンネルによる通信

移籍は参照をそのまま送る場合と同様の実行時間となっ性能を計測するため、予備評価を行なった。評価環境はた。このとことから、例えば配列の各要素が不変オブジェ

Intel(R) Core(TM) i5-3380M CPU@ 2.90GHz 上で動作クトである、などの情報を備える配列を特別に用意する、する Windows 7 上で動作する VirtualBox 上で動作する

もしくは特殊共有オブジェクトとして特別なデータ構造を

Ubuntu 14.04.1 LTS で行なった。対象とする Ruby 処理系

用意する、といった工夫が有望であると考えられる。

は ruby 2.4.0dev [x86 64-linux] を用いた。CPU は 4 ハードウェアスレッドをサポートするが、VirtualBox では 2
ハードウェアスレッドを仮想的に提供する、という環境である。

6. 関連研究
我々は、Ruby に並列化を導入するために、いくつかの先行研究を行なってきた。

予備評価は、2 つのアプリケーションを用いて行なった。

まずは、[5] では、素直に Ruby のスレッドを並列に実フィボナッチ数を求める関数を実行する 4 つのワーカー

行する処理系の実装方法について論じている。しかし、ス

Guild で実行させるアプリケーションと、整数を含めた配レッドをそのまま利用することになり、その難しさを引き

列を足し合わせる 4 つのワーカー Guild で実行させるアプ継ぐことになり、使いやすい並行・並列プログラミングをリケーションの 2 つである。

支援することができない。

なお、まだ Ruby プログラムを他の Guild で利用するこ[7] では、複数プロセスでの並列処理を、共有メモリを用とはできないため、ワーカー Guild の処理は C で記述しいて支援するためのシステムを提案した。本報告の移籍のた。つまり、Ruby の処理におけるマルチ Guild 化のオー

アイデアは、この論文で提案している move と同様である

バヘッドを計測するものではない。このモデルにおける通が、この論文では任意のデータについての実現方法を十分

信のオーバヘッドを見積もるための予備評価となっている。

に示しておらず、またあるタイミングでは move でも可変状態が複数のプロセスで共有してしまうという問題がある

5.1 フィボナッチ数を求めるアプリケーション
このアプリケーションでは、メイン Guild から整数値をことが判明した。そのため、本報告で示したような実現手法を提案した。

渡し、各 Guild で処理を行なう。実験の結果、表 1 に示す[6] では、空間を完全にわける、いわゆるサンドボックス

とおり、整数値の受け渡しはオーバヘッドがほぼなく、ほのような VM 環境を一つのインタプリタプロセス中に作成

ぼ 2 倍の性能向上という、理想的な評価結果を得た。

する方法について論じた。VM 間の通信については、CoWを活用することで一部のデータについて高速に転送できる

5.2 配列の要素を足し合わせるアプリケーション
10 万要素の配列をメイン Guild から 4 つのワーカ Guildに渡し、各ワーカー Guild で和を計算する、というアプリ
ケーションである。
結果を表 2 に示す。Multi/ref というのは、本来は許さことを示した。Guild と違い、オブジェクトスペースは完
全に分離している。なお、プログラミング言語 Racket における Place[4] が同様の提案を行なっている。
このように、プロセス、MVM、スレッドと、並行実行
単位の粒度が異なる並行・並列処理機構について検討してないが、参考のため、可変オブジェクトの参照をそのままきたが、プロセスや MVM では、各単位が分離しすぎて送る、マルチスレッドを模した場合、Multi/mvoe は移籍

おり共有が難しく、スレッドでは共有しすぎてしまい、ス

62016-3-(5): 情報処理学会プログラミング研究会 発表資料 2016 年 10 月 26 日

レッドプログラミングの困難さが生じる。Guild は、これらの中間に位置し、不変オブジェクトや、特別に考慮して

7. 今後の課題

いる特殊共有オブジェクトは共有できるが、通常の可変オ
ブジェクトは共有させない、というモデルとなっている。
ほとんどのプログラムは、このモデルで問題無く記述できると想定しているが、例えば共有するクラスのロードや変更が、他の Guild が動作と並行に起こった場合、本当に使いやすいかは議論する必要がある。
実装の面から考えると、GC については、オブジェクトの空間を完全に分離したほうが、一度に対象とするオブジェクトの数は減るため性能が良くなる。また、メソッド呼び出しにおけるインラインキャッシュなど、実行に必要な可変データを共有しないよう分離するほうが、性能は向上するが、オブジェクトのコピーが行なわれるためメモリ効率は低下する。Guild は、いくらかの共有オブジェクトやデータを各 Guild が共有するためメモリ効率は高いが、同期のためのオーバヘッドが必要になる。GC はすべてのGuild のオブジェクトを管理するため、性能は低くなる。
これは、メモリ効率を優先し、性能を犠牲にする選択だと言える。
移籍は、オブジェクト指向における所有権・エイリアスの管理手法の一手法と捉えることもで、この分野は多くの研究ががある [1]。しかし、多くの研究では、型システムを拡張することによって実現し、実行前に解析を終了する
ことで、実行時オーバヘッドを生じない。本研究では、静的な型を持たない動的なプログラミング言語 Ruby において、どのように実現するか議論している点で、これらの研究とは異なる。
所有権の管理を、型チェックを用いずに、ランタイムでチェックを行なう研究も存在する [2], [3]。これらは、参照のたびに所有権を確認することで実現している。そして、
その動的なオーバヘッドをいかに削減するか、というのが重要な要素技術として提案されている。本報告で提案した
移籍は、参照時にチェックする必要がなく、これらの手法に比べその点で有利である。ただし、移籍のたびに参照先

まだ諸々の実装を終えていないため、まずは実装することが急務である。設計は示したが、実際のプログラミング
を通じて、可変オブジェクトが複数の Guild で意図せず共有してしまうといったミスがないかなど、問題をあぶり出し、使い勝手を検証していく必要がある。
言語仕様の検討としては、クロージャの受け渡しをどのようにするかが問題である。例えば、他の Guild に行なって欲しい手続きとして、クロージャを渡すことは自然の要求である。しかし、クロージャは読み書き可能な可変オブジェクトであるため、そのままでは転送できず、コピーを行なわなければならない。この問題は、Guiild を作成し、その Guild で最初に起動されるスレッドの処理をどのように指定するか、という問題にも通じる。Java のように、Guild ごとにクラスを指定するような方法も考えられるが、毎回そのためにクラスを作成、指定せねばならず、煩雑である。Ruby におけるスレッドのように、ブロックを指定し（クロージャを渡して）Guild の最初に実行する手続きを指定したいが、可変オブジェクトの受け渡しとなる。例
えば、クロージャを不変オブジェクトとして成立させるよ
うな新しい文法を導入する必要があるかもしれない。

[6] で示したとおり、現在の MRI の設計（C 言語レベル）
では、最大限の性能を引き出すことができないため、コンテキストへのポインタを第一引数に必ず指定するような方法が有効である。そのためには、MRI のソースコードの設計を大幅に改め、また C 拡張を記述するための Ruby C

API の仕様を刷新する必要がある（ただし、互換背のために、現在利用している C API は、互換レイヤとして残す必要がある）。この変更は、決めてしまえば機械的に行なうことができる。しかし、C API の prefix である rb_ を、別の prefix を与えねばならず、その名前付けがとくに困難である *2 。

8. おわりにも含めてすべて可変オブジェクトであるかのチェックを行
ない、新規オブジェクトを生成するという点で負荷がかかる。また、移籍前後でオブジェクトのアイデンティティを保つことができないという欠点がある。しかし、転送・移籍の回数は、オブジェクトの参照よりも十分に少なく、またオブジェクトのアイデンティティに依存したプログラムはほぼ無いだろうとの予測から、この方式を提案した。また、いくつかの場合（例えば C 拡張）において、このチェックでは可変オブジェクトの意図しない共有を回避することが難しい（回避するためには大きなオーバヘッドを生じる）
ことがわかったため、この手法を採らなかった。

本報告では、現在 Ruby 3 のために検討中の、新しい並行・並列処理を記述するための機構である Guild について紹介した。まだ、実装を十分に行なっておらず、構想段階であるため、開発を進めながら、より良いものに育てていきたい。皆様の忌憚ないご意見を頂くことを期待している。
なお、本報告には図を用いた説明、利用方法や、想定している具体的なプログラム例などの詳細が記述されていないが、発表スライドにそれらを含めるので、参考にされたい。本報告の最新版、および発表スライドは

http://www.atdot.net/~ko1/activities/ にて公開予*2

Ruby コミュニティでは、メソッドやデータ構造の名前付けに多くの苦労が払われている。Guild という名前も、数年越しで検討した結果提案した名前であるが、すでに多くの異論を頂いている。

7

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

定である。
参考文献
[1]

[2]

[3]

[4]

[5]

[6]

[7]

Clarke, D., Noble, J. and Wrigstad, T.(eds.): Aliasing in
Object-Oriented Programming. Types, Analysis and Verification, Lecture Notes in Computer Science, Vol. 7850,
Springer (2013).
Claudel, B., Sabah, Q. and Stefani, J.-B.: Simple Isolation
for an Actor Abstract Machine, Formal Techniques forDistributed Objects, Components, and Systems (Graf, S.
and Viswanathan, M., eds.), Lecture Notes in Computer
Science, Vol. 9039, Springer, pp. 213–227 (2015).
Gordon, D. and Noble, J.: Dynamic Ownership in a Dynamic Language, Proceedings of the 2007 Symposium
on Dynamic Languages, DLS ’07, New York, NY, USA,ACM, pp. 41–52 (online), DOI: 10.1145/1297081.1297090(2007).
Tew, K., Swaine, J., Flatt, M., Findler, R. B.
and pdinda@northwestern.edu, P. D.: Places: Adding
Message-passing Parallelism to Racket, SIGPLAN
Not., Vol. 47, No. 2, pp. 85–96 (online), DOI:
10.1145/2168696.2047860 (2011).
笹田耕一，松本行弘，前田敦司，並木美太郎：Ruby 用仮想マシン YARV における並列実行スレッドの実装，情報処理学会論文誌 (PRO)，Vol. 48, No. SIG 10(PRO33), pp.
1–16 (2007).
笹田耕一，卜部昌平，松本行弘， 平木敬：Ruby 用マル
チ仮想マシンによる並列処理の実現，情報処理学会論文誌
プログラミング（PRO），Vol. 5, No. 2, pp. 25–42 (2012).
中川博貴，笹田耕一：Ruby オブジェクトの効率的なプロ
セス間転送・共有機構の設計と実装，情報処理学会論文誌
プログラミング（PRO），Vol. 5, No. 4, pp. 1–16 (2012).

8