view paper/chapter5.tex @ 74:e6e2f606c68d

update
author anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Mon, 18 Feb 2019 17:24:39 +0900
parents 47bfecf6ae6b
children 29bb4dca94ea
line wrap: on
line source

\chapter{CbCMoarVMの評価}
前章までに、 MoarVMの一部処理をCbCでの書き換えを検討した。
本章では、 CbCMoarVMの評価を行う。

\section{命令処理のモジュール化}
MoarVMの命令コードディスパッチ部分は、 当初はcase文やラベルgotに変換されるため、 1ファイルの記述せざるを得なかった。
書き換えたCodeGearは、 関数の様にCbCから扱う事が可能である。
MoarVMの命令コードディスパッチでは、 命令に対応する数値を利用して、 case文又は配列から実行するラベルなどを取り出していた。
CbCMoarVMでは、 CodeGearの集合である配列を用意している。
この配列の登録順のみ対応させれば、 CbCMoarVM内の命令に対応するCodeGearの書く場所は問わなくなる。
そのため、 命令処理部分を別ファイルに書き出すなどのモジュール化が可能となった。

モジュール化が可能となったことで、 ディスパッチ部分の処理と実際に実行する命令で関数を分離出来た。
これにより、 ソースコード上の可読性や、 適切なスコープ管理などがオリジナルのMoarVMと比較し可能となった。

\section{CbCMoarVMのデバッグ}
主要なデバッガであるgdbやlldbでは、 関数には直接break pointを設定する事が可能である。
MoarVMのバイトコードディスパッチ部分はcase文やラベルgotoに変換され、 関数として扱う事が出来ない。
従って、 命令に対応する処理部分でbreak pointを設定を行う場合、 まず処理が書かれているMVM\_interp\_run関数にbreak pointを設置する必要がある。
プロセス起動後、 関数が書かれているinterp.cファイル中の行番号を指定してbreak pointを付けなければならない。
また、ディスパッチでは数値又はラベルを利用している。
この事から、 ソースコード\ref{origi_breakpoint}に示すように、 デバッガ上で直接どの命令を実行するか確認をする事が困難である。

\lstinputlisting[frame=lrbt, label=origi_breakpoint, caption=MoarVMのbreak pointトレース時の表示]{./codes/src/origin_breakpoint.txt}


CodeGearは関数として扱う事ができる為、 break pointとして直接設定する事が可能である。
また、 gdbなどのデバッガは、 実行するべき関数名を表示する事が可能であるため、 CodeGearの名前も表示可能である。
CodeGear名は命令と対応している為、 CodeGear名から命令名を推測する事が可能となった。
実際にデバッガ上の表示を、 ソースコード\ref{cbc_breakpoint}に示す。


\lstinputlisting[frame=lrbt, label=cbc_breakpoint, caption=CbCMoarVMのbreak pointトレース時の表示]{./codes/src/trace_cbc.txt}

\section{パフォーマンス}
オリジナルのバイトコードインタプリタ、 CbCMoarVMバイトコードインタプリタの両方で速度を測定した。
検証には、Perl6よりも軽量であることからNQPを選択した。

使用するNQPプログラムは、ソースコード\ref{fib_nqp}に示すフィボナッチ数列を求める例題と、 ソースコード\ref{incre_nqp}に示すインクリメントを単純ループで続ける例題を選択した。

\lstinputlisting[frame=lrbt, label=incre_nqp, caption=インクリメントを繰り返すNQPのサンプルコード]{./codes/incre.nqp}

複数回計測を行った結果を、 それぞれ表\ref{table:cbc}と表\ref{table:cbc_loop}に示す。

\begin{table}[tbp]
    \caption{フィボナッチ数列を求める例題}
\label{table:cbc}
\centering
\begin{tabular}{|r|r|} \hline
MoarVM & CbCMoarVM \\ \hline
1.379sec & 1.636sec  \\
1.350sec & 1.8043sec \\
1.346sec & 1.787sec  \\ \hline
\end{tabular}
\end{table}

\begin{table}[tbp]
    \caption{単純ループを計算する例題}
\label{table:cbc_loop}
\centering
\begin{tabular}{|r|r|} \hline
MoarVM & CbCMoarVM \\ \hline
7.499sec & 6.135sec  \\
7.844sec & 6.362sec  \\
6.746sec & 6.074sec  \\ \hline
\end{tabular}
\end{table}

結果から、 再帰呼び出しを多用しているフィボナッチ数列を求める例題では、 オリジナルのVMより低速なパフォーマンスが得られた。
CbCMoarVMでは現在モジュール化は行っているが、 具体的な速度上昇に対する実装は行っていない。
その為、 通常のMoarVMより低速になる事を想定していたが、 表\ref{table:cbc_loop}に示すように、 単純ループを求める場合は高速化した。
これはループ文を利用する際に実行される処理が、 CPUのキャッシュに収まったために高速化したと考えられる。
この事から、CbCMoarVMでは、 CPUキャッシュに命令を乗せる事が可能であれば、 現在の状況でもMoarVMよりパフォーマンスが向上する利点がある。

\section{Threaded Code}

\section{欠点}

CbCはC言語の下位言語であり、 一種のアセンブラの様な言語である。
通常のC言語では、 関数呼び出しで隠蔽されているデータを意識してCodeGearに接続しなければならない。
その為、 C言語で実装を行うより実装で利用する処理やCodeGearの数が増えてしまう。

今回の実装では、 MoarVMの情報をまとめた構造体のポインタを受け渡す実装で行った。
別の実装法として、構造体のポインタでなく、 構造体そのものを受け渡す実装に変更し、 挙動を確認した。
この構造体のメンバは、 一部メンバの内部で同じメンバを参照している。
ポインタの受け渡しでは同期が出来ているが、 構造体そのものを渡してしまうと同期が出来ない。
その為、 構造体のメンバを、 ソースコード\ref{cbc_sync}に示すように、 都度代入しなおす必要が生じた。
この様にCbCを適応した場合、 データの受け渡しを非常に意識する必要がある。

\lstinputlisting[frame=lrbt, label=cbc_sync, caption=CbCMoarVMのデータ同期]{./codes/sync.c}

現在CbCコンパイラでは、 CodeGearでの不要なスタック操作命令を完全に排除できていない為、 CbCの利点が損なわれている箇所が存在する。
CbCからC言語レベルの処理に戻る際は環境付き継続を行う必要があるケースが有り、 この実装を行うためにコードの量や複雑さが高まってしまうとも考えられる。

CbCMoarVMそのものも、 MoarVMの実装に手を加えている為、 MoarVMのアップデートに追従する必要がある。