From 2ffbba186095cdb69cf61205fd9beaa1a011d5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=BE=E6=B5=A6=20=E7=9F=A5=E4=B9=9F=20Matsuura=20Tomoy?= =?UTF-8?q?a?= Date: Fri, 29 Nov 2024 09:59:27 +0000 Subject: [PATCH] fixed misc --- src/main.tex | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main.tex b/src/main.tex index 66dca3b..4bf23dd 100755 --- a/src/main.tex +++ b/src/main.tex @@ -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{命令セット} \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} と表現される. -擬似コードでは,\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の構造} \label{sec:vmstructure} @@ -174,7 +174,7 @@ Luaのupvalue演算に加えて,$delay$ と $feed$ 式のコンパイルを処 クロージャが \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命令へのコンパイル例} @@ -217,7 +217,7 @@ fn dsp(x){ twodelay(x,400)+twodelay(x,800) } \end{verbatim} -\caption{\label{fig:fbdelay_code}{mimiumにおける,\texttt{self}とtexttt{delay}を組み合わせたフィードバックディレイの関数を複数回使用するコードの例.}} +\caption{\label{fig:fbdelay_code}{mimiumにおける,\texttt{self}と\texttt{delay}を組み合わせたフィードバックディレイの関数を複数回使用するコードの例.}} \end{figure} \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}命令を複数回に分割して実行することで対応ができる.}. -以前のmimiumの実装では,内部状態を関数の呼び出しに応じた木構造として生成していたが,本VMの命令セットでは内部状態の読み書き位置を相対的なオフセットとして表現することで,メモリレイアウトは単純な配列として表現可能になり,各関数はどの関数から呼び出されるのかについて知る必要がなくなる.これはLuaのVMがクロージャ変換で全ての自由変数の参照を静的に解決するのではなく,upvalueとしてコールスタック上の相対的な位置として扱い,部分的にランタイムに上位値の解決を任せることでコンパイラの実装を簡略化しているのと近しいアプローチと言える. +以前のmimiumの実装では,内部状態を関数の呼び出しに応じた木構造として生成していたが,本VMの命令セットでは内部状態の読み書き位置を相対的なオフセットとして表現することで,メモリレイアウトは単純な配列として表現可能になり,各関数はどの関数から呼び出されるのかについて知る必要がなくなる.これはLuaのVMがクロージャ変換で全ての自由変数の参照を静的に解決するのではなく,上位値としてコールスタック上の相対的な位置として扱い,部分的にランタイムに上位値の解決を任せることでコンパイラの実装を簡略化しているのと近しいアプローチと言える. \begin{figure}[ht] \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}キーワードを使用している.}. -以前の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 \begin{verbatim} CONSTANTS[100,1,0,2] @@ -472,7 +472,7 @@ Rust側では関数名(\verb|gen_sampler_mono|),その関数の型,Rust \begin{acknowledgment} -mimiumの初期開発は,2019年度未踏IT人材発掘・育成事業の支援の元行われたほか,オープンソースソフトウェアとして様々な方からコントリビューションを受けている.また本研究は,日本学術振興会科研費若手研究『音楽と工学の相互批評的実践としての「音楽土木工学」の研究 』(23K12059)の助成を受けている.ここに感謝の意を表する. +mimiumの開発は,2019年度未踏IT人材発掘・育成事業の支援の元行われたほか,オープンソースソフトウェアとして様々な方からコントリビューションを受けている.また本研究は,日本学術振興会科研費若手研究『音楽と工学の相互批評的実践としての「音楽土木工学」の研究 』(23K12059)の助成を受けている.ここに感謝の意を表する. \end{acknowledgment} % BibTeX を使用する場合 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%