資格道場 › レッスン一覧 › ソフトウェア
ソフトウェア・8/10・12分

コンパイラとインタプリタ(言語処理系)

ゴール

コンパイラ・インタプリタ・アセンブラなど「言語処理系」の役割を見分け、コンパイラの処理の順番(字句解析→構文解析→意味解析→最適化→コード生成)と最適化の目的を答えられるようになる。インタプリタ方式とコンパイル方式の処理時間を比べる計算問題も、式を立てて解けるようになる。

コンパイラの最適化は本試験で5回、免除試験で12回出ていて、このレッスンでいちばん出る知識です。説明文から処理系の名前を当てる問題がほとんどなので、比較表を一度頭に入れれば確実に取れます。

このレッスンで稽古する(用語チェック・過去問)

結論:人が書いたプログラムを、機械が動かせる形にする方法は2つ

人が書いたプログラム(原始プログラム=ソースコード)は、そのままではCPUが実行できません。そこで機械語に変える言語処理系(言語プロセッサ)を使います。やり方は大きく2つで、コンパイラは先に全部を翻訳してから実行し、インタプリタは1行ずつ解釈しながら実行します。

たとえるなら、コンパイラは「本を丸ごと翻訳して出版する翻訳家」、インタプリタは「話を聞きながらその場で訳す通訳」です。翻訳本は作るのに時間がかかりますが、出来上がれば何度でもすぐ読めます。通訳はすぐ始められますが、毎回その場で訳す手間がかかります。

コンパイラ インタプリタ
やること 原始プログラムを解析し、目的プログラム(オブジェクトコード)を生成する 原始プログラムを解釈しながら実行する
翻訳した結果 残る(機械語のファイルができる) 残らない(実行して終わり)
実行速度 速い 遅い(毎回解釈するため)
始めるまでの手間 コンパイル時間がかかる すぐ実行できる
処理の順番 全体を翻訳 → 実行 1命令ずつ 読込み → 解析 → 実行 をくり返す
コンパイラ方式原始プログラム翻訳目的プログラム実行1回目実行2回目実行3回目翻訳は1回だけ。目的プログラムが残るインタプリタ方式原始プログラム実行1回目:解析しながら実行実行2回目:解析しながら実行実行3回目:解析しながら実行実行のたびに解析し直すので遅い
コンパイラは翻訳を1回だけ行い、残った目的プログラムを何度でも実行する。インタプリタは実行のたびに原始プログラムを解析し直す。

コンパイラと一緒に出る「周りのプログラム」

コンパイラが作ったオブジェクトコードは、それだけではまだ動きません。部品をリンカ(連係編集プログラム)でつないでロードモジュール(実行できる形のプログラム)にし、ローダで主記憶(メモリ)に読み込んではじめて実行できます。

試験では、この3つの説明がコンパイラの選択肢にまぎれ込んできます。

名前 やること 選択肢でのキーワード
コンパイラ 原始プログラム → オブジェクトコード 「解析し」「オブジェクトコードを生成」「目的プログラムに翻訳」
リンカ オブジェクトコードなどを結合 → ロードモジュール 「ロードモジュールを生成」
ローダ ロードモジュールを主記憶に読み込む 「主記憶にロード」「メモリに格納」
インタプリタ 原始プログラムを解釈しながら実行 「その都度解釈し,実行」「解釈しながら実行」
原始プログラムコンパイラ翻訳するオブジェクトコード(部品)リンカ部品を結合するロードモジュールローダ主記憶へ読み込む主記憶(メモリ)実行ここで初めて動く
原始プログラムはコンパイラ・リンカ・ローダを順に通り、主記憶に読み込まれてはじめて実行できる。

いろいろな言語プロセッサ(説明文から名前を当てる)

コンパイラとインタプリタ以外にも、名前と説明を入れ替えた選択肢がよく出ます。入力と出力が何かに注目すると見分けられます。

名前 入力 → 出力 ひとことで言うと
アセンブラ アセンブリ言語 → 機械語 機械語に近い言語専用の翻訳機
コンパイラ 高水準言語 → 機械語(目的プログラム) まとめて翻訳
インタプリタ 高水準言語 → (その場で実行) 解釈しながら実行
トランスレータ ある言語・処理系用のプログラム → 別の言語・処理系用のプログラム プログラムの書き換え・変換機
ジェネレータ 条件(パラメタ)→ プログラム 条件を指定するとプログラムを自動生成
クロスコンパイラ 原始プログラム → 別の種類のコンピュータ用の目的プログラム 自分以外の機械向けに翻訳
エミュレータ 他機種用のプログラム → (その場で実行) 他機種の命令をまねて実行する(翻訳はしない)

クロスコンパイラは、たとえばパソコン上で家電やスマホの組込み機器用のプログラムを作るときに使います。命令形式が違うコンピュータ向けのプログラムを「作る」のがクロスコンパイラ、他機種の命令を「まねて動かす」のがエミュレータです。

コンパイラの中の処理順序:字句→構文→意味→最適化→コード生成

コンパイラの内部は、決まった順番の5段階で動きます。日本語の文を読むときに、①単語に区切る ②文法をチェックする ③意味が通るか確かめる、という順に進むのと同じです。

flowchart LR
  A["原始プログラム"] --> B["字句解析"]
  B --> C["構文解析"]
  C --> D["意味解析"]
  D --> E["最適化"]
  E --> F["コード生成"]
  F --> G["目的プログラム"]
段階 やること 選択肢でのキーワード
① 字句解析 文字の並びを、意味のある最小の単位(字句=トークン。変数名・数値・記号など)に区切る 「文字の列を最小の構成要素の列に変換」
② 構文解析 字句の並びが文法どおりかを調べる 「文法に基づいて」「文法誤りがないか」
③ 意味解析 変数の宣言と使用の対応、データ型の整合性を調べる 「宣言と使用を対応付け」「型の整合性」
④ 最適化 実行が速く(小さく)なるようにプログラムを変形する 「レジスタ割付け」「不要な演算を省略」
⑤ コード生成 目的プログラム(機械語)を出力する 「目的コード生成」

覚え方は「字・構・意・最・コ(じ・こう・い・さい・こ)」。最初は必ず字句解析です。

最適化の目的は「実行時間の短縮」(最頻出)

最適化とは、コンパイラが生成するプログラムを、実行したときに速く(あるいは小さく)なるように作り変えることです。たとえば、ループの中で毎回同じ計算をしていたら外に出す、使わない計算を消す、よく使う値を高速なレジスタ(CPU内部の小さな記憶場所)に置く、といった処理をします。

大事なのは「誰の何が速くなるのか」です。速くなるのはできあがったプログラムを実行するときであって、コンパイル作業そのものではありません。むしろ最適化をするぶん、コンパイル時間は長くなりがちです。

試験で正解になる言い方は次のどれかです。

  • 「プログラムの実行時間を短縮する」
  • 「実行時の処理効率を高めたオブジェクトコードを生成する」

不正解の定番は「デバッグを容易にする」「保守性を改善する」「目的プログラムを生成する時間を短縮する」「インタプリタ用の中間コードを生成する」「別アーキテクチャ用のコードを生成する(=クロスコンパイラ)」です。

最適化前ループ(n回くり返す)a×b を計算(毎回同じ)結果を足すa×b を n回 計算する最適化最適化後a×b を1回だけ計算← ループの外ループ(n回くり返す)結果を足すa×b の計算が n回 → 1回 に減る
ループの中で毎回している同じ計算をループの外に出すと、計算はn回から1回に減り、実行時間が短くなる。

処理時間の比較計算:「固定でかかる時間」と「行数に比例する時間」に分ける

インタプリタは準備が要らない代わりに1行ごとの実行が遅く、コンパイル方式は最初に固定の手間がかかる代わりに実行が速い、という関係です。だからプログラムが長いほどコンパイル方式が有利になり、どこかで逆転します。その逆転する行数を求めるのがこの計算問題です。

解き方の型は次のとおりです。

  1. 行数を n と置く
  2. 条件の「100行あたり○秒」を 1行あたりに直す
  3. 各方式の合計時間を「固定時間 + 1行あたり時間 × n」で書く
  4. 「コンパイル方式 < インタプリタ方式」の不等式を解き、答えは切り上げる

コンパイル方式の合計は「オーバヘッド+コンパイル時間+実行時間」の3つの和です。1つでも足し忘れると、ひっかけの選択肢に誘導されます(下の例題を参照)。

インタプリタが有利ここからコンパイルが有利時間行数 n固定の手間インタプリタ方式コンパイル方式逆転する行数コンパイル方式の合計=オーバヘッド+コンパイル時間+実行時間
コンパイル方式は固定の手間から緩やかに増え、インタプリタ方式は0から急に増えるので、逆転する行数より右ではコンパイル方式が有利になる。
例題 1

何行以上ならコンパイル方式の方が速いか

Java などのバイトコードプログラムをインタプリタで実行する方法と,コンパイルしてから実行する方法を,次の条件で比較するとき,およそ何行以上のバイトコードであれば,コンパイル方式の方がインタプリタ方式よりも処理時間(コンパイル時間も含む)が短くなるか。 〔条件〕 (1) 実行時間はプログラムの行数に比例する。 (2) 同じ 100 行のバイトコードのプログラムをインタプリタで実行すると 0.2 秒掛かり,コンパイルしてから実行すると 0.003 秒掛かる。 (3) コンパイル時間は 100 行当たり 0.1 秒掛かる。 (4) コンパイル方式の場合は,プログラムの行数に関係なくファイル入出力,コンパイラ起動などのために常に 0.15 秒のオーバヘッドが掛かる。 (5) プログラムファイルのダウンロード時間など,そのほかの時間は無視して考える。

ア 50
イ 75
ウ 125
エ 155

出典:平成23年度 特別試験 基本情報技術者試験 午前 問23

解き方と答えを見る
  1. 行数を n 行と置く。条件(1)より、実行時間は行数に比例する。
  2. インタプリタ方式:100行で0.2秒 → 1行あたり 0.2 ÷ 100 = 0.002秒。合計は 0.002n 秒。
  3. コンパイル方式の実行時間:100行で0.003秒 → 1行あたり 0.003 ÷ 100 = 0.00003秒。
  4. コンパイル時間:100行で0.1秒 → 1行あたり 0.1 ÷ 100 = 0.001秒。
  5. オーバヘッド:行数に関係なく常に 0.15秒。
  6. コンパイル方式の合計 = 0.15 + 0.001n + 0.00003n = 0.15 + 0.00103n 秒。
  7. コンパイル方式の方が短くなる条件:0.15 + 0.00103n < 0.002n
  8. 両辺から 0.00103n を引く:0.15 < 0.002n − 0.00103n = 0.00097n
  9. n > 0.15 ÷ 0.00097 ≒ 154.6
  10. 行数は整数なので切り上げて 155行以上。
  11. 検算:n=155 のとき インタプリタ 0.002×155 = 0.31秒、コンパイル 0.15 + 0.00103×155 = 0.15 + 0.15965 = 0.30965秒 → コンパイル方式の方がわずかに速い。

答え:エ(155行)。オーバヘッドを忘れると0.001n+0.00003n<0.002nとなり、どの行数でもコンパイルが速いことになる。コンパイル時間を忘れると0.15/0.00197≒76行となり、イ(75)付近に誘導される。

要点(試験直前に読む)

  • コンパイラ=原始プログラムを解析して目的プログラム(オブジェクトコード)を生成。インタプリタ=解釈しながら実行(読込み→解析→実行)。
  • コンパイラの処理順序は「字句解析→構文解析→意味解析→最適化→コード生成」。最初は字句解析。
  • 意味解析=変数の宣言と使用の対応・型の整合性。構文解析=文法チェック。字句解析=最小単位に区切る。
  • 最適化の目的は「実行時間の短縮(実行時の処理効率向上)」。コンパイル時間の短縮でもデバッグ支援でもない。
  • クロスコンパイラ=別の命令形式のコンピュータ向けに生成。ジェネレータ=パラメタからプログラムを生成。トランスレータ=別の処理系用に変換。
  • 処理時間の比較:コンパイル方式=オーバヘッド+コンパイル時間+実行時間。1行あたりに直して不等式を解き、答えは切り上げる。

ひっかけ

  • 「ロードモジュールを生成」はリンカ、「主記憶に格納・ロード」はローダの説明。コンパイラの選択肢にまぎれていても選ばない。
  • 最適化の選択肢に「目的プログラムを生成する時間を短縮」が出たら誤り。速くなるのは実行時で、コンパイルは逆に時間がかかる。
  • 処理順序の問題では、字句解析と構文解析を入れ替えた選択肢が定番。「単語に区切ってから文法を見る」と考えれば、字句が先とわかる。
  • 言語プロセッサの説明では、名前と説明を入れ替えた選択肢が出る。「解釈しながら実行」はインタプリタ、「他処理系用に変換」はトランスレータ、「他機種の命令をまねて実行」はエミュレータ。
  • 時間比較の計算でオーバヘッドやコンパイル時間を足し忘れると、50・75・125のような小さい値の選択肢に誘導される。3つの時間を全部足したか確認する。

用語(18)

原始プログラム げんしプログラム(ソースプログラム)
人がプログラム言語で書いた、翻訳する前のプログラム。
⚠ 目的プログラム(翻訳した後の機械語)と逆。「原始=元」と覚える。
目的プログラム もくてきプログラム(オブジェクトプログラム/オブジェクトコード)
コンパイラが原始プログラムを翻訳して作った機械語のプログラム。
⚠ ロードモジュールは、目的プログラムをリンカで結合して実行できる形にしたもの。
言語処理系 げんごしょりけい(言語プロセッサ)
プログラム言語で書かれたプログラムを、コンピュータが実行できる形に翻訳または解釈する仕組みの総称。
コンパイラ コンパイラ(compiler)
原始プログラム全体を解析し、目的プログラム(オブジェクトコード)を生成する言語処理系。
⚠ インタプリタは翻訳結果を残さず解釈しながら実行する。「生成」「翻訳」ならコンパイラ。
インタプリタ インタプリタ(interpreter)
原始プログラムを1命令ずつ解釈しながら実行する言語処理系で、読込み→解析→実行の順にくり返す。
⚠ エミュレータは他機種の命令をまねて実行するもの。「その都度解釈して実行」ならインタプリタ。
アセンブラ アセンブラ(assembler)
アセンブリ言語(機械語と1対1に近い記号の言語)で書いたプログラムを機械語に変換する言語処理系。
⚠ 「他の処理系用の原始プログラムに変換」するのはトランスレータの説明で、アセンブラではない。
トランスレータ トランスレータ(translator)
ある言語や処理系向けのプログラムを、別の言語や処理系向けのプログラムに変換する言語処理系。
⚠ 「解釈しながら実行」はインタプリタの説明で、トランスレータではない。
ジェネレータ ジェネレータ(generator/プログラムジェネレータ)
入力・処理・出力などの条件をパラメタで指定すると、目的に合ったプログラムを自動で生成する言語処理系。
⚠ 「パラメタで指示して生成」がキーワード。コンパイラは原始プログラムを入力にする。
クロスコンパイラ クロスコンパイラ(cross compiler)
あるコンピュータ上で動き、命令形式が異なる別のコンピュータ向けの目的プログラムを生成するコンパイラ。
⚠ エミュレータは他機種の命令をまねて「実行」し、目的プログラムは作らない。
エミュレータ エミュレータ(emulator)
別の機種の命令をまねて実行し、他機種用のプログラムを動かす仕組み。
⚠ クロスコンパイラは他機種用のプログラムを「生成」する。
リンカ リンカ(linker/連係編集プログラム)
複数の目的プログラムやライブラリを結合して、ロードモジュールを作るプログラム。
⚠ コンパイラの選択肢に「ロードモジュールを生成する」と出たらリンカの説明。
ローダ ローダ(loader)
実行可能なプログラム(ロードモジュール)を主記憶に読み込むプログラム。
⚠ 「主記憶にロード」「メモリに格納」はローダ。コンパイラやインタプリタではない。
字句解析 じくかいせき
コンパイラの最初の段階で、文字の並びを変数名・数値・記号などの意味を持つ最小単位(字句)に区切る処理。
⚠ 構文解析は区切った字句の並びが文法どおりかを調べる。順番は字句→構文。
構文解析 こうぶんかいせき
字句の並びが言語の文法に合っているかを調べ、文法誤りを見つける処理。
⚠ 意味解析は型や宣言の対応を調べる。「文法」なら構文、「型・宣言」なら意味。
意味解析 いみかいせき
変数の宣言と使用を対応付け、演算のデータ型が整合しているかを調べる処理。
⚠ 構文解析は文法のチェックだけをする。
最適化 さいてきか(optimization)
生成するプログラムの実行時間を短く(または容量を小さく)するために、不要な演算の省略やレジスタ割付けを行う処理。
⚠ 速くなるのは実行時であって、コンパイル時間やデバッグのしやすさではない。
コード生成 コードせいせい(目的コード生成)
コンパイラの最後の段階で、目的プログラム(機械語)を出力する処理。
オーバヘッド オーバヘッド(overhead)
本来の処理とは別に、準備や管理のためにかかる余分な時間や資源。
⚠ 計算問題では行数に関係ない「固定時間」として扱い、足し忘れに注意する。

このレッスンの過去問

このレッスンで稽古する