コンパイラとインタプリタ
プログラミング言語の種類としてコンパイラ言語とインタプリタ言語という分類もあるのですが、実はその中間のものも多かったりします。プログラミング言語は人間がコードを書くためのもので、それをコンピュータ(CPU)が、そのままで実行できるわけではないので、何らかの方法で変換が必要になります。コンパイルは一般に「翻訳」とも呼ばれ、ソースコードをコンパイラと呼ばれるプログラムを使って、一括してCPUが実行できる命令に変換することです。それに対してインタプリトは「通訳」と呼ばれることもあり、コードを実行する際に、その都度ソースコードを部分的に変換して実行します。インタプリタが言語の処理系であり変換結果としてCPUが実行できる命令を出力することはありません。
インタプリタの代表格としては古のBASIC言語がありますが、この言語もキーボードからコードが入力され行末のリターンキーが叩かれた時点で、その文字列が解釈されBASICの命令部分は中間コードと呼ばれるバイトに変換され、数値や行番号も場合によれば2進の値に直されます(処理系によって異なる)。
N系BASICの中間言語仕様
BASICのプログラムをテープなりディスクに保存するときには、特別なオプションを付けない限り、この中間言語に変換されたものが保存されます。そして中間言語は処理系によって異なるために他の処理系に移植する際には変換が必要になるわけです。初期のBASICでは単純にコードを変換するだけでしたが、例えば行番号をそのまま持っているだけだと実行するたびにその番号を検索する必要があるので、一度、検索して呼び出すアドレスが決定できたら、今度は中間言語を変更して見つかったアドレスを保存することで処理を高速化するような処理系が増えてきました(これが理由でサブルーチンは若い行番号を使うべしというハックが行われていたのですが、その意味が無くなっても習慣はなかなか廃れませんでした)。
コンパイラにしても、必ずしもソースコードをそのコンピュータが搭載している実際のCPUのマシン語に変換するのではなく、仮想的なCPUに対する命令に変換し、その仮想的なCPUの命令をインタプリタとして実行するものも多いです。古いところではUSCD p-Systemがそれで、仮想マシンのコードはp-codeと呼ばれていました。8ビットや16ビットなCPUの時代は単純な処理であっても、マシン語にすると複雑な命令を組み合わせる必要があって、コンパイルするとコードがとても長くなってしまうという悩みがあったのですが、仮想マシンの命令は強力なので、短いコードで表現できるので少ないメモリやディスクを活用するうえでは重宝しました。但し速度の面ではネイティブ(実際のCPUの命令に変換する)なコンパイラに比べれば物足りなくはあり、関数の単位で実際のマシン語に変換して使うというケースもありました(そのような指定も出来た)。
pコードマシン
その後もVBなどでは、この仕組みが使われていて仮想マシンのインタプリタはVBのランタイムとして組み込まれて使われていました。そして、いろいろなCPUで使われる Java 言語も、JavaVMと呼ばれる仮想マシンに対するコンパイラで、CPU毎にVMを用意すれば同じコンパイラが使えるというので、今でも使われています。
またインタプリタの中間言語も python もインタプリタですから、ソースコードを一旦、中間言語に翻訳してから実行する仕組みとなっていて、中間言語をバイトコードと呼んで使われています。
Pythonの.pycファイル入門
最近pycファイル見ないなーと思っていたら、python3でpycacheに隔離されていた話
プログラミング言語の中間表現って幅広いと思った話
そしてもう一つ、最近の主流なのがJIT(just-in-time compiler)と呼ばれる方法で、実行に必要なソースコードすべてを予めコンパイルするのではなく、ソースコードを実行する段階になって必要な部分だけを都度、コンパイルする方法です。もともとインタプリタは即座に実行できるので頻繁にソースコードを更新しても余分なコストは発生せず、コンパイラは更新の都度、仮に一度も実行することが無くてもコンパイルという手間がかかります。何度も繰り返し実行するのであればトータルではコンパイルしてしまったほうが遥かに効率が良いのですが、特に開発段階ではその手間は無視できません。またコンパイラは特定の環境(CPUやOS)で動作するようなコードを生成するので、いろいろな環境で実行させるのであれば、それぞれのバイナリを用意する必要があります。JITは、それらの「いいとこ取り」で、コンパイルを実行する段階まで引き伸ばしたうえで、コンパイルしてしまった部分に関しては、その次からは予めコンパイルしたものと同じようなパフォーマンスを出すことが出来るわけです。この手法があまりに広く使われるようになったので、今までのようなコンパイル手法をわざわざ「事前コンパイラ」とまで呼ぶようになりました。
実行時コンパイラ
事前コンパイラ
JITコンパイラとは?メリットや仕組みなどをわかりやすく解説
という訳で、今ではいろいろなプログラミング言語に対して、どれがインタプリタで、どれがコンパイラだと単純には言えなくなってきているのですが、言語にとっては処理が静的で事前にコードが決定できるのか、実行時になって初めてわかる情報を使って処理が決定するのかの違いが大きく、事前の処理の内容や頻度がわかっていれば高度な最適化の余地があるのですが、そもそも滅多に実行されないようなコードを頑張って最適化しても、その恩恵は殆どありません。実行時にデータを見ながら能率的な処理を探すほうが今ではトータルの意味で最適化なのかもしれませんし、もしかしたらAIが動的にコードを生成しながら実行するように進化するのかもしれません。
ヘッダ画像はAI画像生成に、この記事の文章をプロンプトにして作ってみました。