異なるアプローチ
私は、N-PrologのパフォーマンスをSWI-Prologに近づける方法を考えてきました。考えられるアイデアとしては、型特化型の統一、決定的述語の識別、尾再帰の最適化などがあります。しかし、N-PrologとSWI-Prologの間には大きなパフォーマンスギャップがあり、それを埋めるのは簡単ではありません。
これを野球に例えるなら、SWI-Prologは速球を投げるピッチャーで、N-Prologはそのスピードに対抗できません。そこで、速球に頼るのではなく、カーブボールを投げることにしました。そのアイデアは、Cの埋め込み機能を導入し、決定的な計算をCを使用して高速に実行できるようにすることです。
アッカーマン関数
以下のコードは、アッカーマン関数の計算例です:
アッカーマン関数は本質的に計算負荷が高く、大量の再帰呼び出しを必要とします。明らかに、Prologはこのような計算には向いていません—バックトラッキング情報を保存する必要があるため、すぐにスタックオーバーフローが発生します。
しかし、アッカーマン関数は決定的な計算であり、バックトラッキングを必要としません。Prologでは、決定性を強制して効率を改善するためにカットを戦略的に配置することができます。しかし、カットの過剰な使用はPrologコードを乱雑にし、命題論理のエレガンスを損ないます。
では、Cで単純な再帰関数として書いてみてはどうでしょうか?このアプローチでは、ack(4,1)のような計算でも瞬時に実行できます。
コンパイルメカニズム
N-PrologはすでにPrologコードをCに変換し、それを動的にコンパイルしてリンクします。これにより、Prologコード内にCを埋め込むのが非常に簡単になります。コンパイラのAPIはCOMPILER.mdに記載されており、このAPIを活用することで、Cでリスト処理を自由に実行できます。
Prologで表現するのが難しい操作や、高いパフォーマンスを要求する操作は、Cで実装することができます。
速球ではなくカーブボール
このC埋め込み機能により、N-Prologは新しいアプローチを導入しましたが、これは単なるカーブボールです。バッターを混乱させるかもしれませんが、SWI-Prologのような速球ピッチャーのスピードには到底及びません。
では、SWI-Prologはどのようにしてその驚異的なスピードを実現しているのでしょうか?どうやら、Just-In-Time(JIT)コンパイルを適用しているようです。今後はさらに最適化のアイデアを探り、N-Prologのパフォーマンスをさらに向上させていきたいと思います。
追記
現在、この仕組みを使ってTensorflowとの接続、tcltk openglとの接続を検討中です。