fixed misc

This commit is contained in:
2024-11-29 09:59:27 +00:00
parent cdcab4baa1
commit 2ffbba1860

View File

@@ -128,7 +128,7 @@ $\lambda_{mmm}$ を実行するための仮想マシン(VM)モデルとその命
逆に,クロージャ変換(ラムダ・リフティング)と呼ばれる,内部関数が参照するすべての外部変数を解析し,内部関数の隠し引数として追加するような処理を使えば,実行時のパフォーマンスを向上できる.しかし,コンパイラの実装においてこの変換は比較的複雑になる. 逆に,クロージャ変換(ラムダ・リフティング)と呼ばれる,内部関数が参照するすべての外部変数を解析し,内部関数の隠し引数として追加するような処理を使えば,実行時のパフォーマンスを向上できる.しかし,コンパイラの実装においてこの変換は比較的複雑になる.
LuaのVMは2つの方法の中間的なアプローチである上位値\textit{upvalue}という概念を採用しており,命令 \texttt{GETUPVALUE}\texttt{SETUPVALUE} を追加することで実行時に外部変数を動的に参照できるようになっている上位値を使ったコンパイラとVMの実装はクロージャの完全な変換よりも単純でありながら大幅な性能低下を避けることができるこのアプローチではクロージャが元の関数の文脈からエスケープしない限り上位値はヒープメモリではなくコールスタックへの間接的参照で読み取られる\cite{nystrom2021}さらに上位値の利用は他のプログラミング言語との相互運用性を促進する例えばLua用の外部ライブラリをC言語で実装する場合プログラマはC API経由でランタイムのコールスタック上の値だけでなくLuaランタイムの上位値にアクセスすることもできる LuaのVMは2つの方法の中間的なアプローチである\emph{上位値upvalue}という概念を採用しており,命令 \texttt{GETUPVALUE}\texttt{SETUPVALUE} を追加することで実行時に外部変数を動的に参照できるようになっている上位値を使ったコンパイラとVMの実装はクロージャの完全な変換よりも単純でありながら大幅な性能低下を避けることができるこのアプローチではクロージャが元の関数の文脈からエスケープしない限り上位値はヒープメモリではなくコールスタックへの間接的参照で読み取られる\cite{nystrom2021}さらに上位値の利用は他のプログラミング言語との相互運用性を促進する例えばLua用の外部ライブラリをC言語で実装する場合プログラマはC API経由でランタイムのコールスタック上の値だけでなくLuaランタイムの上位値にアクセスすることもできる
\subsection{命令セット} \subsection{命令セット}
\label{sec:instruction} \label{sec:instruction}
@@ -150,9 +150,9 @@ $\lambda_{mmm}$ のVMはLua VMバージョン5以降と同様にレジ
命令の一覧を図\ref{fig:instruction}に示す基本的な算術演算などは省略した命令の表記はLua VMのドキュメントに概説されている形式\cite[p.13]{ierusalimschy2005}に従った各命令は32ビットの整数として表現される\footnote{命令の種類に8ビットを割り当てているのは後の拡張のためでもあるが実装の際Rustのタグ付き共用体(\texttt{enum})として実装するのが簡単なためという理由も大きい.}.図\ref{fig:instruction}の擬似コードでは左から順に8ビットずつ命令の種類最大3つのオペランドのリストが並ぶ3つのオペランドがそれぞれ符号なし8ビット整数として使用される場合\texttt{A B C}のように表現される.オペランドが符号付きの整数として使用される場合は,接頭辞 \texttt{s} が付く複数のオペランドフィールドを組み合わせて16ビット24ビットの値にする場合接尾辞 \texttt{x}\texttt{xx} が付加される.例えば,\texttt{B}\texttt{C} を結合して符号付き 16 ビット値として扱う場合,\texttt{sBx} と表現される. 命令の一覧を図\ref{fig:instruction}に示す基本的な算術演算などは省略した命令の表記はLua VMのドキュメントに概説されている形式\cite[p.13]{ierusalimschy2005}に従った各命令は32ビットの整数として表現される\footnote{命令の種類に8ビットを割り当てているのは後の拡張のためでもあるが実装の際Rustのタグ付き共用体(\texttt{enum})として実装するのが簡単なためという理由も大きい.}.図\ref{fig:instruction}の擬似コードでは左から順に8ビットずつ命令の種類最大3つのオペランドのリストが並ぶ3つのオペランドがそれぞれ符号なし8ビット整数として使用される場合\texttt{A B C}のように表現される.オペランドが符号付きの整数として使用される場合は,接頭辞 \texttt{s} が付く複数のオペランドフィールドを組み合わせて16ビット24ビットの値にする場合接尾辞 \texttt{x}\texttt{xx} が付加される.例えば,\texttt{B}\texttt{C} を結合して符号付き 16 ビット値として扱う場合,\texttt{sBx} と表現される.
擬似コードでは,\texttt{R(A)} はコールスタック上の現在の関数のベースポインタ + \texttt{A}の位置にあるデータを示す.\texttt{K(A)}はコンパイル済みプログラムの静的変数セクションの \texttt{A} 番目のエントリを指し,\texttt{U(A)}は現在の関数の \texttt{A} 番目のupvalueにアクセスする. 擬似コードでは,\texttt{R(A)} はコールスタック上の現在の関数のベースポインタ + \texttt{A}の位置にあるデータを示す.\texttt{K(A)}はコンパイル済みプログラムの静的変数セクションの \texttt{A} 番目のエントリを指し,\texttt{U(A)}は現在の関数の \texttt{A} 番目の上位値にアクセスする.
Luaのupvalue演算に加えて,$delay$$feed$ 式のコンパイルを処理するために, \texttt{GETSTATE}\texttt{SETSTATE}\texttt{SHIFTSTATE}\texttt{DELAY}の4つの新しい演算が導入されている Luaの上位値演算に加えて,$delay$$feed$ 式のコンパイルを処理するために, \texttt{GETSTATE}\texttt{SETSTATE}\texttt{SHIFTSTATE}\texttt{DELAY}の4つの新しい演算が導入されている
\subsection{VMの構造} \subsection{VMの構造}
\label{sec:vmstructure} \label{sec:vmstructure}
@@ -174,7 +174,7 @@ Luaのupvalue演算に加えて$delay$ と $feed$ 式のコンパイルを処
クロージャが \texttt{RETURN} 命令によって元の関数コンテキストから抜け出すとき,事前に挿入された \texttt{CLOSE} 命令によってアクティブな上位値はスタックからヒープメモリに退避させられる.これらの上位値は,特にネストしたクロージャを含む場合には,複数の場所から参照される可能性がある.したがって,これらの上位値が使用されなくなった時にはガベージコレクションでメモリを解放する必要がある\footnote{現在のmimiumの実装では参照カウント方式でメモリを開放している} クロージャが \texttt{RETURN} 命令によって元の関数コンテキストから抜け出すとき,事前に挿入された \texttt{CLOSE} 命令によってアクティブな上位値はスタックからヒープメモリに退避させられる.これらの上位値は,特にネストしたクロージャを含む場合には,複数の場所から参照される可能性がある.したがって,これらの上位値が使用されなくなった時にはガベージコレクションでメモリを解放する必要がある\footnote{現在のmimiumの実装では参照カウント方式でメモリを開放している}
$\lambda_{mmm}$ は値呼びラムダ計算であり,再代入が存在しないので, \texttt{SETUPVALUE}命令は省略される.再代入が許可されているような意味論を追加する場合,オープンなものも含めupvalueは共有メモリセルとして実装される必要がある\footnote{現在のmimiumの実装では初期実装からの使用を引き継いで\texttt{let}で宣言された変数に対する再代入が許されているため,実際には\texttt{SETUPVALUE}も使用されているが,本稿では議論を単純化するため省略した.関数に値を渡す際に値はコピーされるため,関数の外側の状態を変更する方法はクロージャがキャプチャした変数への再代入のみとなっている.} $\lambda_{mmm}$ は値呼びラムダ計算であり,再代入が存在しないので, \texttt{SETUPVALUE}命令は省略される.再代入が許可されているような意味論を追加する場合,オープンなものも含め上位値は共有メモリセルとして実装される必要がある\footnote{現在のmimiumの実装では初期実装からの使用を引き継いで\texttt{let}で宣言された変数に対する再代入が許されているため,実際には\texttt{SETUPVALUE}も使用されているが,本稿では議論を単純化するため省略した.関数に値を渡す際に値はコピーされるため,関数の外側の状態を変更する方法はクロージャがキャプチャした変数への再代入のみとなっている.}
\subsection{VM命令へのコンパイル例} \subsection{VM命令へのコンパイル例}
@@ -217,7 +217,7 @@ fn dsp(x){
twodelay(x,400)+twodelay(x,800) twodelay(x,400)+twodelay(x,800)
} }
\end{verbatim} \end{verbatim}
\caption{\label{fig:fbdelay_code}{mimiumにおける\texttt{self}とtexttt{delay}を組み合わせたフィードバックディレイの関数を複数回使用するコードの例.}} \caption{\label{fig:fbdelay_code}{mimiumにおける\texttt{self}\texttt{delay}を組み合わせたフィードバックディレイの関数を複数回使用するコードの例.}}
\end{figure} \end{figure}
\begin{figure}[ht] \begin{figure}[ht]
@@ -280,7 +280,7 @@ fn dsp (x)
この例ではVM命令の中で\texttt{GETSTATE} 命令で \texttt{self} を参照した後,または別の内部状態付き関数を呼び出した後に,\texttt{SHIFTSTATE} 命令が挿入され,次の(非クロージャ)関数呼び出しに備えて内部状態の読み書き位置を移動している.関数が終了する前には,再び\texttt{SHIFTSTATE}命令を使用して,読み書き位置を現在の関数の実行開始時の状態にリセットしている.図\ref{fig:fbdelay_spos}は,\texttt{twodelay}関数の実行中に\texttt{SHIFTSTATE}命令によって内部状態読み書きポインタがどのように遷移するかを示している.\texttt{SHIFTSTATE}操作の引数はワードサイズ符号付き24ビットの数値でありディレイのためのワードサイズは読み取り位置インデックス書き込み位置インデックスリングバッファの長さの値の3つが付加されるため最大遅延時間+3となる\footnote{符号付き24bit整数の最大値はサンプリングレート192kHzなら42秒程度である場合によってはより大きな値を扱う必要があるかもしれないがその場合は\texttt{SHIFTSTATE}命令を複数回に分割して実行することで対応ができる.} この例ではVM命令の中で\texttt{GETSTATE} 命令で \texttt{self} を参照した後,または別の内部状態付き関数を呼び出した後に,\texttt{SHIFTSTATE} 命令が挿入され,次の(非クロージャ)関数呼び出しに備えて内部状態の読み書き位置を移動している.関数が終了する前には,再び\texttt{SHIFTSTATE}命令を使用して,読み書き位置を現在の関数の実行開始時の状態にリセットしている.図\ref{fig:fbdelay_spos}は,\texttt{twodelay}関数の実行中に\texttt{SHIFTSTATE}命令によって内部状態読み書きポインタがどのように遷移するかを示している.\texttt{SHIFTSTATE}操作の引数はワードサイズ符号付き24ビットの数値でありディレイのためのワードサイズは読み取り位置インデックス書き込み位置インデックスリングバッファの長さの値の3つが付加されるため最大遅延時間+3となる\footnote{符号付き24bit整数の最大値はサンプリングレート192kHzなら42秒程度である場合によってはより大きな値を扱う必要があるかもしれないがその場合は\texttt{SHIFTSTATE}命令を複数回に分割して実行することで対応ができる.}
以前のmimiumの実装では内部状態を関数の呼び出しに応じた木構造として生成していたが本VMの命令セットでは内部状態の読み書き位置を相対的なオフセットとして表現することでメモリレイアウトは単純な配列として表現可能になり各関数はどの関数から呼び出されるのかについて知る必要がなくなるこれはLuaのVMがクロージャ変換で全ての自由変数の参照を静的に解決するのではなくupvalueとしてコールスタック上の相対的な位置として扱い,部分的にランタイムに上位値の解決を任せることでコンパイラの実装を簡略化しているのと近しいアプローチと言える. 以前のmimiumの実装では内部状態を関数の呼び出しに応じた木構造として生成していたが本VMの命令セットでは内部状態の読み書き位置を相対的なオフセットとして表現することでメモリレイアウトは単純な配列として表現可能になり各関数はどの関数から呼び出されるのかについて知る必要がなくなるこれはLuaのVMがクロージャ変換で全ての自由変数の参照を静的に解決するのではなく上位値としてコールスタック上の相対的な位置として扱い,部分的にランタイムに上位値の解決を任せることでコンパイラの実装を簡略化しているのと近しいアプローチと言える.
\begin{figure}[ht] \begin{figure}[ht]
\centering \centering
@@ -310,9 +310,9 @@ fn dsp(){
さらに別の例として図\ref{fig:filterbank_good}に高階関数\texttt{filterbank}を使うものを示す.この関数は,別の関数\texttt{filter}(入力と周波数を引数として受け取り結果の数値を返す)を引数として受け取り,\texttt{filter}のインスタンスを\texttt{n}個複製し,それらをの計算結果を足し合わせるような関数を返すものである\footnote{以前のmimiumの仕様\cite{Matsuura2021}では,変数束縛と破壊的代入の構文は同じ(\texttt{x = a})だった.しかし,新しいバージョンの構文では,変数の束縛に\texttt{let}キーワードを使用している.} さらに別の例として図\ref{fig:filterbank_good}に高階関数\texttt{filterbank}を使うものを示す.この関数は,別の関数\texttt{filter}(入力と周波数を引数として受け取り結果の数値を返す)を引数として受け取り,\texttt{filter}のインスタンスを\texttt{n}個複製し,それらをの計算結果を足し合わせるような関数を返すものである\footnote{以前のmimiumの仕様\cite{Matsuura2021}では,変数束縛と破壊的代入の構文は同じ(\texttt{x = a})だった.しかし,新しいバージョンの構文では,変数の束縛に\texttt{let}キーワードを使用している.}
以前のmimiumのコンパイラは内部状態のツリー全体をコンパイル時に静的に決定する必要があったためこのような内部状態を引数に取る関数をコンパイルすることができなかったしかし$\lambda_{mmm}$ のVMではこれを動的に解決することができる\ref{fig:bytecode_filterbank}このコードをVM命令にコンパイルした例を示す\texttt{filterbank}の1行目の再帰呼び出しや引数として渡された関数や(\texttt{filter}のように)upvalueとして取得した関数の呼び出しは,\texttt{CALL}命令ではなく\texttt{CALLCLS}命令を使って実行される.ここで,\texttt{GETSTATE}命令や \texttt{SETSTATE} 命令はこの関数では使用されない.これは,\texttt{CALLCLS} 命令が実行される際に,使用する内部状態ストレージが動的に切り替わるためである.内部状態を変更する命令は引数として与えられた\verb|filter|を呼び出す際に実行される. 以前のmimiumのコンパイラは内部状態のツリー全体をコンパイル時に静的に決定する必要があったためこのような内部状態を引数に取る関数をコンパイルすることができなかったしかし$\lambda_{mmm}$ のVMではこれを動的に解決することができる\ref{fig:bytecode_filterbank}このコードをVM命令にコンパイルした例を示す\texttt{filterbank}の1行目の再帰呼び出しや引数として渡された関数や\texttt{filter}のように)上位値として取得した関数の呼び出しは,\texttt{CALL}命令ではなく\texttt{CALLCLS}命令を使って実行される.ここで,\texttt{GETSTATE}命令や \texttt{SETSTATE} 命令はこの関数では使用されない.これは,\texttt{CALLCLS} 命令が実行される際に,使用する内部状態ストレージが動的に切り替わるためである.内部状態を変更する命令は引数として与えられた\verb|filter|を呼び出す際に実行される.
\begin{figure}[ht] \begin{figure}[t]
\centering \centering
\begin{verbatim} \begin{verbatim}
CONSTANTS[100,1,0,2] CONSTANTS[100,1,0,2]
@@ -472,7 +472,7 @@ Rust側では関数名\verb|gen_sampler_mono|その関数の型Rust
\begin{acknowledgment} \begin{acknowledgment}
mimiumの初期開発は2019年度未踏IT人材発掘・育成事業の支援の元行われたほかオープンソースソフトウェアとして様々な方からコントリビューションを受けているまた本研究は日本学術振興会科研費若手研究『音楽と工学の相互批評的実践としての「音楽土木工学」の研究 』23K12059の助成を受けているここに感謝の意を表する mimiumの開発は2019年度未踏IT人材発掘・育成事業の支援の元行われたほかオープンソースソフトウェアとして様々な方からコントリビューションを受けているまた本研究は日本学術振興会科研費若手研究『音楽と工学の相互批評的実践としての「音楽土木工学」の研究 』23K12059の助成を受けているここに感謝の意を表する
\end{acknowledgment} \end{acknowledgment}
% BibTeX を使用する場合 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % BibTeX を使用する場合 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%