view Paper/chapter3.tex @ 19:7d8aa97ff754

update Paper
author e165727 <e165727@ie.u-ryukyu.ac.jp>
date Sun, 16 Feb 2020 13:28:38 +0900
parents 4c121d8b0354
children 0137c5074de4
line wrap: on
line source

\chapter{Raku によるAbyss の実装}
提案手法で述べた, 同一ホスト内で終了せずに実行を続けるサーバープロセスを立ち上げ, このサーバープロセス上で立ち上げておいたコンパイラに実行するファイル名を転送し,サーバー上でコンパイルを行う手法に沿い『Abyss Server』を実装した.
\section{サーバーの構成}
Abyss サーバーは クライアント側から投げられた Rakuスクリプト を実行するためのサーバーである.
図\ref{fig:AbyssExecute}は,  Abyss サーバーを用いたスクリプト言語実行手順である. 
Abyss サーバーはユーザーが Raku を直接立ち上げるのではなく, まず 図\ref{fig:AbyssExecute} 右側の Abyss サーバーを起動し, ユーザーは Abyss サーバーにファイルパスをソケット通信で送り, Abyss サーバーがファイルを開き実行し, その実行結果をユーザーに返す. 

この手法を用いることで, サーバー上で事前に起動した Rakudo を再利用し, 投げられた Raku スクリプトの実行を行うため, Rakudo の起動時間を短縮できると推測できる. 

\begin{figure}[H]
     \begin{center}
     \includegraphics[width=80mm]{fig/abyss.pdf}
     \end{center}
     \caption{Abyssサーバーを用いたスクリプト言語実行手順}
    \label{fig:AbyssExecute}
\end{figure}

\newpage
\section{Abyss Server側の実装}
[\ref{Server}]はAbyss サーバーのソースコードである.
Abyssサーバーは起動すると, まず自身にファイルパスを転送するためのソケットを生成し, その後ファイルパスを受け取り実行して . 出力結果をソケットに書き込む

\lstinputlisting[label=Server,  caption= Abyss Server側の source code ]{code/abyss.p6}

\section{Abyss Client側の実装}
ユーザーは Abyss Server を起動後, Client側からファイルパスをサーバーに送信し, [\ref{Client}]のようにSocketに書き込まれた実行結果を読み取る.

\lstinputlisting[label=Client,  caption=Abyss Client側の source code ]{code/client.p6}


\section{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の機能を使う必要がある \\

\section{Raku の EVAL}
Raku ではEVAL関数[\ref{code3}]があり文字列を Raku のソースコード自身として評価できる

Raku では, EVALは通常は使用できないようになっており, MONKEY-SEE-NO-EVAL という pragma を実行することで使うことができるようになる.
EVALFILE はファイルパスを受け取るとファイル開き, バイト文字列に変換し読み込む, その後読み込んだバイト文字列にデコードし, ファイルパスの文字列を読み込み, ファイルの中身を EVAL と同様に解釈する.

[\ref{Server}] の2行目にある MONKEY−SEE−NO−EVAL は Perl6 上で EVALFILE を使用可能にする pragma である.

\lstinputlisting[label=code3,  caption=evalのサンプルコード]{code/eval.p6}
%通常、自分でプロセス立ち上げてPerl6を実行する際は,

\section{NativeCall}
Rakuでは Native Call という標準ライブラリを用いて, Cのライブラリを扱うことが可能である.

[\ref{Server}]で利用したCのライブラリは以下のものである
\begin{itemize}
\item dup(int fd) - dup() は引数で指定したファイルディスクリプタを複製して, 未使用のファイルディスクリプタから最小番号を割り当てます
\item dep2(int newfd, int oldfd) - dup2() は oldfd を newfd に関連づけます.
\item close(int fd) - close() は指定されたfile descreptorを閉じます.
\end{itemize}


\section{出力をSocketに書き込む際に起きた問題}
Raku では標準出力をSocketに書き込む API が Raku 側からは提供されていない.

そのため, 通常 Code \ref{Server} のようにEVALFILEを実行した際, 出力はそのまま Server 側の標準出力に返ってしまう.

この問題を解決するために, Raku の NativeCall を用いて Cのライブラリを使用した.

Code \ref{Server} の26行目で dup2() を用いて 標準出力に Socket の file descreptor を割り当て,
その後 EVALFILE を実行することで出力を Client側に返すことに成功した.
 

\section{通常実行との速度比較}
今回は,提案手法での実行速度と通常実行での実行速度, この二つの速度の比較を行う
題材として行うのはhelloworldを出力するだけのプログラムとフィボナッチ数列の例題である.

実験結果
\begin{table}[H]
  \begin{center}
    \begin{tabular}{|l|l|} \hline
      Language& Time \\  \hline
      通常実行 & 0.2695 sec \\
      提案手法 & 0.0238 sec \\ \hline
    \end{tabular}
  \end{center}
  \caption{通常実行と提案手法の速度比較}
\end{table}
提案手法は通常実行に比べて約10倍早い実行結果になった