前回はuLispのアセンブラを試用。Arm Thumb命令のネイティブ・コードを生成してくれるアセンブラです。さて今回はこのアセンブラの上に乗る「Lispコンパイラ」を使ってみます。なんとuLisp関数(使える関数はサブセットだけれども)をネイティブコードにコンパイルしてくれます。実行速度は超高速化、当然。
※Lispと一緒 投稿順 index はこちら
※実機確認は Raspberry Pi Pico2で行ってます。
※使用させていただいとります uLisp のバージョンは 4.6b (Arm用)です。
uLispのコンパイラ
まだ、simple experimental という位置づけですが、uLispにはコンパイラも備わっています。uLisp関数の定義をアセンブラ経由でネイティブコードに落としてくれるもの。御本家マニュアルページが以下に。
A Lisp compiler to ARM written in Lisp
上記を拝見するにコンパイラ本体はCommon Lisp上でも走るみたいなので、「クロス・コンパイル」も可能といえば可能みたいです。ただし、後段のアセンブラは uLisp独特のスペシャル・フォームである defcode関数に依存しているので「セルフ開発」で使うのがお楽かと。
コンパイラ本体のソースの在処は以下に。
Lisp compiler to ARM Thumb Assembler
上記をロードすればコンパイル用の関数が使えるようになります。ただしコンパイラの中から前回使ってみたアセンブラを呼び出すので、ネイティブコードに落として「セルフ開発」するにはアセンブラもロードしておかねばなりませぬ。
コンパイラ使用のメモリ領域は、上記のArm Thumb用で約1500ノード(セル?)くらいです。これにArmアセンブラの使用部分約2000が加わるので当方処理系上では約3500くらいの消費でした。ラズパイPico2機上の uLisp ではフリーが23000ほどあるので、余裕。ホントか?
ベンチマーク、竹内関数
さて、コンパイラのベンチマークとして使われていたのが「竹内関数」です。電電公社(老人には馴染み深いお名前だが、最近の若者、いや中年は知らない?NTTの前身だぜ)の竹内先生御発明らしいです。しかし、uLispの上記ページに掲載されている関数を拝見すると、オリジナルではなく、ジョン・マッカーシー先生版みたいデス。マッカーシー先生こそ、神として崇められている?大先生です。知らないとモグリよ。
竹内関数をロードして、素の(インタプリタ実行の)uLisp上で走らせたところが以下に。
おお、3.5秒もかかっておりますなあ。でも何気に解説ページでの値7秒よりずっと速いです。ラズパイPico2、やっぱり速いね。
コンパイル
さてコンパイルは簡単、以下のようにコンパイルしたい関数名を comile関数に渡してやるだけ。ただし、コンパイル可能な関数には制限強いので、竹内関数のようなちんまい整数のやりとりで再帰が重い関数などには向く感じがしますが、ちょっと高級な関数を使うとコンパイルできんかもです。その辺は上記の解説ページを見てくだされや。
(compile 'tak)
実際にコンパイルかけた先頭部分が以下に。前回のuLispアセンブラにArm Thumコードが渡っているようです。
これでuLispのS式だった筈の tak 関数は、ネイティブコードに変換されてしまったみたい。知らんけど。
コンパイル後の実行結果
さて、コンパイル後、もう一度同じ引数を与えて tak関数を呼び出してみます。
おっと、結果は15ミリ秒とな。素の実行では3.5秒もかかっていたので、まさに2けた違いの速さじゃ。コンパイラの御利益あらたか。