Финал блока фундамента — и прямой ответ на вопрос, оставшийся открытым в конце главы 04: кто именно переводит код высокого уровня в машинные команды, и когда это происходит.
Компилятор читает весь исходный код программы и заранее переводит его целиком в машинный код конкретного процессора, создавая отдельный исполняемый файл. Дальше этот файл запускается напрямую — никакого переводчика во время выполнения уже не нужно, процессор выполняет уже готовый машинный код. Классические компилируемые языки — C, Pascal, современные C++, Rust, Go.
Плюс — скорость выполнения: программа уже переведена в машинный код, дополнительных затрат на перевод во время работы нет. Минус — прямое следствие темы главы 04: скомпилированный файл работает только на той архитектуре процессора (и часто только на той операционной системе), под которую он был скомпилирован — для другой платформы нужна отдельная компиляция того же исходного кода заново.
Интерпретатор не создаёт отдельный исполняемый файл заранее — вместо этого он читает исходный код и выполняет его непосредственно, переводя и сразу исполняя каждую часть программы по ходу дела. Классический пример — ранние диалекты BASIC (глава 06), современные примеры — многое в мире скриптовых языков.
Плюс — переносимость самого исходного кода: тот же файл со скриптом можно запустить на любой машине, где установлен подходящий интерпретатор, независимо от архитектуры процессора — перевод происходит каждый раз заново на месте запуска. Минус — обычно ниже скорость выполнения по сравнению с заранее скомпилированным кодом, потому что перевод выполняется каждый раз при запуске, а не один раз заранее.
Python часто называют "интерпретируемым языком" — это упрощение, полезное для общего
понимания, но не полностью точное. На практике стандартная реализация Python (CPython)
сначала компилирует исходный код в промежуточный байт-код — не финальный
машинный код конкретного процессора, а собственный внутренний формат команд для
"виртуальной машины" Python. Уже этот байт-код построчно интерпретируется во время
выполнения. Именно поэтому иногда рядом с Python-файлами можно увидеть файлы с
расширением .pyc — это и есть сохранённый скомпилированный байт-код,
избавляющий от необходимости заново компилировать один и тот же файл при каждом запуске.
Похожий подход (компиляция в промежуточный байт-код + виртуальная машина) использует и
Java, и платформа .NET (C#) — это не редкое исключение, а распространённая архитектура,
сочетающая часть преимуществ обоих миров.
Ещё один шаг в сторону сочетания скорости и переносимости — JIT (just-in-time) компиляция: вместо того чтобы интерпретировать байт-код построчно каждый раз, система отслеживает, какие части кода выполняются особенно часто ("горячие" участки), и компилирует именно их в настоящий машинный код прямо во время выполнения программы, кешируя результат для повторного использования. Современные движки JavaScript в браузерах, PyPy (альтернативная быстрая реализация Python) и виртуальная машина Java активно используют именно такой подход.
У компилируемых языков со строгой типизацией значительная часть ошибок (опечатки в названиях, несовпадение типов данных) обнаруживается на этапе компиляции — программа просто не соберётся в исполняемый файл, пока ошибка не исправлена. У интерпретируемых языков (и Python в их числе) многие ошибки обнаруживаются только в момент, когда интерпретатор реально доходит до этой конкретной строки во время выполнения — если ошибка находится в редко выполняемой ветке кода, она может оставаться незамеченной долгое время, пока эта ветка наконец не сработает на практике. Тема обработки таких ошибок в Python — отдельная глава 21 дальше в этой книге.
| Критерий | Компиляция | Интерпретация |
|---|---|---|
| Скорость выполнения | Обычно выше | Обычно ниже (без JIT) |
| Переносимость исходника | Требует перекомпиляции под платформу | Один файл работает везде, где есть интерпретатор |
| Момент обнаружения ошибок | Много ошибок — на этапе сборки | Часто только при реальном выполнении строки |
| Распространение программы | Готовый исполняемый файл | Нужен установленный интерпретатор у пользователя |
Теперь можно честно ответить на вопрос, что физически происходит при команде вроде
python script.py (подробно команды терминала — блок 2, сам синтаксис Python —
блок 3): интерпретатор Python читает исходный файл, компилирует его в байт-код (при
необходимости кешируя результат), и виртуальная машина Python построчно исполняет этот
байт-код, вызывая по ходу дела уже настоящий машинный код (для базовых операций,
реализованных в самом интерпретаторе, написанном на языке C). Ни одно из слов "просто
интерпретируется" или "просто компилируется" не описывает происходящее полностью честно —
теперь ты знаешь, как это устроено на самом деле.
Пять глав дали общий фундамент: биты, системы счисления, кодировки, понятие программы, компиляция и интерпретация. Следующий блок использует этот фундамент для взгляда назад — в историю языков программирования, начиная с BASIC, где эти самые понятия проявлялись в гораздо более простой и наглядной форме, чем в современных языках.
Книга бесплатна для чтения. Автор — Тариел Бежанидзе, компьютерный мастер в Батуми · tariel.ge · все бесплатные книги. Копировать текст целиком на другие сайты — только со ссылкой на источник.