view Slide/Slide.md @ 25:cde9c8f9b7e4 default tip

update slide
author e165727 <e165727@ie.u-ryukyu.ac.jp>
date Mon, 17 Feb 2020 04:11:41 +0900
parents 27f7561b1135
children
line wrap: on
line source

title: Raku(Perl6)のサーバーを使った高速実行
author: Kouki Fukuda, Shinji Kono
profile: 並列信頼研

## スクリプト言語の高速実行
 - 現在多くのスクリプト言語はインタプリタ型言語である
 - 実行時にインタプリタの立ち上げ, モジュールを読み込み, スクリプトの解釈, スクリプトの実行 といったような処理を担っている.
 - 頻繁にコードを書き換え実行するスクリプト言語では起動時間をできるだけ短くしたい.

![](fig/BootCut.svg)

## インタプリタの再利用
 - 同一ホスト内で終了せずに実行を続けるサーバーを立ち上げ, このサーバー上で立ち上げておいたインタプリタに実行するファイル名を転送し, サーバー上で実行を行う手法を提案する
 - この提案手法に沿って『Abyss サーバー』を実装した.
 - 本研究では現在開発の進んでいる言語である Raku にこの手法を用いて起動時間の改善を行った.

![](fig/AbyssFlow.svg)


<!-- 
## 研究概要
- Raku の実装の一つであるRakudoは、Byte code である MoarVM と、それ上で動作する Raku のsubsetであるnqp (Not Quite Perl)上に構成されている。
- 現状のRakuの実行はRakuで記述されたコンパイラをloadしてJITしながら実行すること自体に時間がかかっている。
- そこで, 同一ホスト内で終了せずに実行を続けるサーバープロセスを立ち上げ, このサーバープロセス上で立ち上げておいたコンパイラに実行するファイル名を転送し, サーバー上でコンパイルを行う手法を提案し実装する.
-->


## Raku と他言語の起動時間の比較
- Raku と他言語の起動時間の比較行なった.

<!--
- 実行環境
```
macOS Mojave version 10.14.5
メモリ8GB
プロセッサ2.7GHz Intel Core i5
```
-->

- perl5,ruby,raku,pythonでhelloworldを出力するプログラムを用いて行なった実行結果である.
<table style="border-collapse: collapse;" border="1" width="400" height="300">
    <tr>
        <th>Language</th>
        <th>Version</th>
        <th>Time</th>
        <th>Ratio</th>
    </tr>
    <tr>
        <td>raku</td>
        <td>2019.03.1</td>
        <td>249 ms</td>
        <td>62.25</td>
    </tr>
    <tr>
        <td>perl5</td>
        <td>v5.18.4</td>
        <td>4 ms</td>
        <td>1</td>
    </tr>
    <tr>
        <td>python</td>
        <td>2.7.10</td>
        <td>13 ms</td>
        <td>3.25</td>
    </tr>
    <tr>
        <td>ruby</td>
        <td>2.3.7p456</td>
        <td>83 ms</td>
        <td>20.75</td>
    </tr>
</table>

Perl5 を基準とすると Raku はその62.25倍と非常に起動時間が遅いことがわかる.

<!--
## Rakudo
- Rakudoとは現在のRakuの主力な実装である.(Raku は言語名, Rakudo はコンパイラ)
- Rakudo は MoarVM, と NQP と呼ばれる Raku のサブセット, NQP と Raku 自身で記述された Raku という構成である.
- Rakudo は MoarVMの他に JVM やJava Script を動作環境として選択可能である.

![](fig/Rakudo.svg)
- Rakudoの構成
-->

<!--
## 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 はSocketを用いて送信した Raku スクリプトを実行するための Server である.
- 下記の図は, Abyss Server を用いたスクリプト言語の実行手順です.

![](fig/Abyss.svg)

## Abyss Server側の実装
- Abyss Server は起動すると, まず自身にファイルパスを転送するためのソケットを生成するその後ファイルを受け取るための待機ループに入る.
```
my $listen = IO::Socket::Unix.new(  :listen,
                                    :localhost<localhost>,
                                    :localport(3333) );
```

- ファイルパスを受け取ると, ファイルを開き実行する.
- また Raku には標準出力をSocketに切り替える機能がないので, Cの標準ライブラリであるdupを用いて標準出力を切り替えている

```
my $backup = dup(1);
loop
{
    my $conn = $listen.accept;
    my $buf = $conn.recv();
    my $sock_msg = $buf;
    close(1);
    dup2($conn.native-descriptor(), 1);
    EVALFILE $sock_msg;
    dup2($backup, 1);
    close($backup);
    $conn.close;
}

$listen.close;
```

## Abyss Client側の実装
- ユーザーは Abyss Server を起動後,ファイルパスをサーバーに送信する.
- Client側ではファイルのパスをSocketに送信しているだけなので,Client側は他の言語で記述することができる

```
my $conn = IO::Socket::Unix.new( :host<localhost>,
                                 :port(3333) );

$conn.print: 'Absolute file path';

my $sock_msg;

while my $buf = $conn.recv(:bin)
{
    $sock_msg = $buf.decode;
    last;
}

say $sock_msg;
```

## 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と同様に解釈する.

<!--
## 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の機能を使う必要がある
-->


## 通常実行と提案手法の速度比較
- 今回は下記3つの実行速度の比較を行う
    - 提案手法でClient側をPerl5で実行する場合 
    - 提案手法でClient側をRakuで実行する場合
    - 通常実行

- 題材として行うのはhelloworldを出力するプログラムとフィボナッチ数列の例題である.

<!--
## 予測
- 前述した通り, Raku はコンパイラの起動に時間がかかっているため, 提案手法を用いることで起動時間分早く実行することができると予測する.
-->

## 実行結果

<table style="border-collapse: collapse;" border="1" width="400" height="300">
    <tr>
        <th>手法e</th>
        <th>Helloworld</th>
        <th>フィボナッチ</th>
    </tr>
    <tr>
        <td>通常実行</td>
        <td>177 ms</td>
        <td>215 ms</td>
    </tr>
    <tr>
        <td>Client(perl5)</td>
        <td>25 ms</td>
        <td>26 ms</td>
    </tr>
    <tr>
        <td>Client(Raku)</td>
        <td>911 ms</td>
        <td>989 ms</td>
    </tr>
</table>

- Client側がperl5の場合は, 通常実行に比べて約10倍早い実行結果になった
- Client側がRakuの場合が異常に遅いが,これはClient側でRakuを起動しているため通常実行の時間+モジュール読み込みの時間がかかっているためだと考えられる

<!--
- 提案手法は通常実行に比べて約7倍早い実行結果になった
-->

## Abyss Serverの利点
- Abyss Serverを用いて実行することで, インタプリタの起動時間を短縮できる.
    - 約10倍早くなる
- 普通のスクリプト言語だと実行するたびにforkして実行しインタプリタの立ち上げという処理になるが, プロセス毎回起動しなくて済む

## Abyss Serverの欠点
- 現在 Abyss Server には 一度スクリプトを実行した後にサーバー内の環境をリセットする機能が存在しないため,スクリプトがサーバー内の環境に影響を及ぼした場合,通常実行と違う挙動をする危険性がある
- 同時に二つ以上のタスクを与えられると実行順のスケジューリングができない
- 異常に長いタスクが投げられた場合, 次のタスクが前のタスクが終わるまで実行ができない
- 起動時のオプションが選択出来ない

<!--
## OS上でスクリプト言語を実行する方法の改善点
- OS上でスクリプト言語を実行する際の最適な方法として,提案手法のように事前に起動したコンパイラを再利用する方法は有効であると考える
- またOS上でスクリプト言語を実行する際に, OS側で用意されてあるべきAPIとしては以下のようなものが挙げられる
    - 提案手法のように一度立ち上げられたインタプリタを立ち上げたままにする
    - 複数回投げられたスクリプトの実行結果もしくはbasic block を保存できる
    - 実行するスクリプトの周りにあるJsonファイルをあらかじめParseしておく
-->

## まとめと今後の課題
- スクリプト言語 Raku の新たな実行方法の提案を行った.

<!--
- Raku にUnix domain socket の実装を行なった.
-->

- またRaku を用いて「Abyss Server」の実装を行なった.
- Abyss Server を用いて実行を行うことで 起動にかかる時間が約10倍早くなることがわかった
- また今後の課題としては以下のようなものが挙げられる
    - 一度投げられたスクリプトをキャッシュで保存しておき,再度実行する際に,そのキャッシュを用いて処理時間を省くような仕組み
    - 複数タスクが投げられた場合の処理の実装