Perl6(Raku)のサーバーを使った高速実行
|
Kouki Fukuda, Shinji Kono
琉球大学
|
研究背景
- 現在開発の進んでいる言語に Perl6(Raku) がある.
スクリプト言語 Raku は任意の VM が選択できるようになっており, 主に利用されている VM に C で書かれた MoarVM が存在する.
- MoarVM は JIT コンパイルなどをサポートしているが, 全体的な起動時間及び処理速度が Perl5 や Python , Ruby などの他のスクリプト言語と比較し非常に低速である.
その為, 現在日本国内では Raku は実務としてあまり使われていない.
- Raku の持つ言語機能や型システムは非常に柔軟かつ強力であるため, 実用的な処理速度に達すれば, 言語の利用件数が向上することが期待される.
研究概要
- Raku の実装の一つであるRakudoは、Byte code である MoarVM と、それ上で動作する Raku のsubsetであるnqp (Not Quite Perl)上に構成されている。
- 現状のRakuの実行はRakuで記述されたコンパイラをloadしてJITしながら実行すること自体に時間がかかっている。
- そこで, 同一ホスト内で終了せずに実行を続けるサーバープロセスを立ち上げ, このサーバープロセス上で立ち上げておいたコンパイラに実行するファイル名を転送し, サーバー上でコンパイルを行う手法を提案し実装する.
Raku と他言語の起動時間の比較
- perl6と他言語の起動時間の比較行なった.
- 実行環境
macOS Mojave version 10.14.5
メモリ8GB
プロセッサ2.7GHz Intel Core i5
Language |
Version |
raku |
2019.03.1 |
perl5 |
v5.18.4 |
python |
2.7.10 |
ruby |
2.3.7p456 |
Raku と他言語の起動時間の比較
- perl5,ruby,raku,pythonでhelloworldを出力するプログラムを用いて行なった実行結果である.
Language |
Time |
raku |
0.249 sec |
perl5 |
0.004 sec |
python |
0.013 sec |
ruby |
0.083 sec |
Raku は 最も早いperl5 の約62.25倍, pythonの約19倍, rubyの約3倍起動速度が遅いことがわかる.
Rakudo
- Rakudoとは現在のRakuの主力な実装である.(Raku は言語名, Rakudo はコンパイラ)
- Raku は仕様と実装が明確に区分されており, Rakudoは実装, roast という Raku の仕様(テストスイートがある)
- Rakudo は MoarVM, と NQP と呼ばれる Raku のサブセット, NQP と Raku 自身で記述された Raku という構成である.
- Rakudo は MoarVMの他に JVM やJava Script を動作環境として選択可能である.
NQP
- NQPとはNot Quite Perl の略で Raku のサブセットである.
- 基本的な文法などは Raku に準拠しているが, 変数を束縛で宣言するなどの違いがある.
- NQPコンパイラ自身もNQPで記述されている
- NQP は MoarVM や JVMの違いを吸収してAPIを提供している
MoarVM
- MoarVM は Raku に特化したVM
- C 言語で実装されている
- JIT コンパイルなどが現在導入されているが, 起動時間などが低速である問題がある
Perl6 の名称変更
- Raku はPerlの次期メジャーバージョンとして設計が始められ, 元々Perl6という名称であったが, Perl6 は言語仕様及び処理実装が Perl5 と大幅に異なっており, 言語的な互換性が存在しないため別名がつけられました.
- 現在有力な処理系である Rakudo から名前を取り Raku という別名がつけられた.
Rakudoの語源
- ちなみに Rakudo の語源は, 楽土と駱駝道で
- 楽土の方は日本語で「楽園」という意味で
- 駱駝道の方は Perlのマスコットキャラクターが🐪だったからです
- http://blogs.perl.org/users/zoffix_znet/2017/07/the-hot-new-language-named-rakudo.html
Rakuが遅い理由
- 通常 Ruby のようなスクリプト言語ではまず YARV などのプロセスVM が起動し,その後スクリプトを Byte code に変換して実行という手順を踏む.
- Rakudo はインタプリタの起動時間及び, 全体的な処理時間が他のスクリプト言語と比較して低速である.
- これは Rakudo 自体が Raku と NQP で書かれているため, MoarVMを起動し, Rakudo と NQP のByte codeを読み取り, Rakudoを起動し, その後スクリプトを読み取り, スクリプトの Byte code 変換というような手順で進むためである.
- また Raku は実行時の情報が必要であり, メソッドを実行する際に invoke が走ることも遅い原因である.
- invoke はMoarVM の method 呼び出しのbyte codeです.
Raku による Abyss Server の実装
- 提案手法に沿い『Abyss Server』を実装した.
- Abyss Server はUnix domain socketを用いて送信した Raku スクリプトを実行するための Server である.
- 下記の図は, Abyss Server を用いたスクリプト言語の実行手順です.
Raku の Unix domain socket 実装
- Unix domain socket でなくINET で実装した場合, 他者からスクリプトを送りつけられる可能性がある.
- そのため今回はUnix domain socket を用いて実装しました.
- Rakuには現在Unix domain socketの実装がないため, Unix domain socket の実装を行なった.
- IO::Socketがroleとして定義されている
- Raku での role は他の言語の interface に相当するものである
- 現状 Raku にはIO::Socket::INETとIO::Socket::Asyncの実装がある
- 先ほど上で説明したようにINETとAsyncはセキュリティの問題で使えない
- IO::Socketを実装した IO::Socket::Unix を実装した
- IO::Socket::Unixの中ではnqpの機能を使う必要がある
通常実行と提案手法の速度比較
- 今回は,提案手法での実行速度と通常実行での実行速度, この二つの速度の比較を行う
- 題材として行うのはhelloworldを出力するだけのプログラムとフィボナッチ数列の例題である.
予測
- 前述した通り, Raku はコンパイラの起動に時間がかかっているため, 提案手法を用いることで起動時間分早く実行することができると予測する.
実行結果
- 通常実行
- 0.2695 sec
- 0.2131 sec
- 0.3143 sec
- 提案手法
- 0.0238 sec
- 0.0219 sec
- 0.0275 sec
- 提案手法は通常実行に比べて約10倍早い実行結果になった
フィボナッチ数列の例題
- 通常実行
- 提案手法
- 先ほどと同様, 提案手法は通常実行に比べて早い結果となり, 約5倍早い実行結果になった
- 内部で処理が走っている時間だけさが縮まったと考えられる
Abyss Server側の実装
- Abyss Server は起動すると, まず自身にファイルパスを転送するためのソケットを生成し, その後ファイルを受け取るための待機ループに入る.
- ファイルパスを受け取ると, ファイルを開き実行する.
unit class Abyss::Server:ver<0.0.1>;
use MONKEY-SEE-NO-EVAL;
use IO::Socket::Unix;
method readeval {
my $listen = IO::Socket::Unix.new(
:listen,
:localhost<localhost>,
:localport(3333)
);
loop {
my $conn = $listen.accept;
while my $buf = $conn.read(1024) {
EVALFILE $buf.decode;
}
$conn.close;
}
}
Abyss Client側の実装
- ユーザーは Abyss Server を起動後, ファイルパスをサーバーに送信する.
use IO::Socket::Unix;
my $conn = IO::Socket::Unix.new( :host<localhost>,
:port(3333) );
$conn.print: 'FILEPASS';
Raku のEVAL
- Raku では EVAL 関数があり文字列を Raku のソースコード自身として評価できる
- Raku では, EVAL は通常は使用できないようになっており, MONKEY-SEE-NO-EVAL という pragma を実行することで使うことができるようになる.
use MONKEY-SEE-NO-EVAL;
EVAL "say { 5 + 5 }"; # OUTPUT: 10
- EVALFILEはファイルパスを受け取ると, ファイルの中身をバイト文字列に変換し, それをEVALと同様に解釈する.
Abyss Serverの利点
- Abyss Serverを用いて実行することで, サーバー上で事前に起動した Rakudo を再利用し, 投げられた Raku スクリプトの実行を行うため, Rakudo の起動時間を短縮できる.
- 一度投げられたスクリプトのバイトコード, もしくは計算結果をキャッシュで保存しておき, 再度実行する際に, そのキャッシュを用いてコンパイル時間を省くような仕組みを入れやすいと考えられる.
- 他の起動時間遅いスクリプト言語や, モジュールの読み込みが遅い言語などにも, 応用しやすいと考えられる.
- 普通のスクリプト言語だと実行するたびにforkして実行しインタプリタの立ち上げという処理になるが, プロセス毎回起動しなくて済む
Abyss Serverの欠点
- 現在 Abyss Server には 一度スクリプトを実行した後にサーバー内の環境をリセットする機能が存在しないため,スクリプトがサーバー内の環境に影響を及ぼした場合,通常実行と違う挙動をする危険性がある
- Raku に eval の出力を socket に切り替える機能が存在しないので出力が Server 側に返ってしまい,ユーザー側に返ってこない
- 同時に二つ以上のタスクを与えられると実行順のスケジューリングができない
- 異常に長いタスクが投げられた場合, 次のタスクが前のタスクが終わるまで実行ができない
- 起動時のオプションが選択出来ない
まとめと今後の課題
- Raku の新たな実行方法の提案,及び実装を行なった.
- Raku にUnix domain socket の実装を行なった.
- Raku の速度改善において, 同一ホスト内でサーバープロセスを生成し,サーバープロセス内であらかじめコンパイラを立ち上げて起き, 実行するファイル名を転送し,サーバープロセス上でコンパイルを行う手法は有効であると考えられる
- 今後は一度投げられたスクリプトをキャッシュで保存しておき,再度実行する際に,そのキャッシュを用いてコンパイル時間を省くような仕組みを実装する予定です