Почему на практике интерпретатор медленнее, чем компилятор?

Разве оба они не должны конвертировать в машинный код в какой-то момент, чтобы выполнить или я пропустил что-то более основное?

РЕДАКТИРОВАТЬ:

Пожалуйста, рассмотрите более сложные схемы интерпретации, например, случаи, когда код переводится в байт-код, а байт-код восстанавливается только при изменении исходного кода, например, реализация на языке CPython Python? Меня не интересуют древние переводчики, которые перестраиваются по строкам...

Благодарю!

Ответ 1

Скомпилированный язык, такой как C, обычно скомпилирован непосредственно в машинный код. Когда вы запускаете код, он выполняется непосредственно процессором.

Полностью интерпретируемый язык, такой как BASIC или PHP, обычно интерпретируется каждый раз, когда он запускается. Когда вы выполняете свой код, CPU выполняет интерпретатор, и интерпретатор читает и выполняет ваш исходный код. (PHP можно классифицировать как полностью интерпретируемый, поскольку, хотя он использует коды операций, они обычно удаляются после выполнения.)

Байт-код, интерпретируемый как Python, скомпилирован из исходного кода в байт-код, который выполняется виртуальной машиной. ЦП запускает виртуальную машину, а виртуальная машина выполняет каждую инструкцию байт-кода. В Python байт-код скомпилирован, когда выполняется первый временной код.

В Java байт-код компилируется перед исполнением. Java VM также имеет специальную функцию, называемую компиляцией Just-in-time. Это означает, что во время выполнения он может скомпилировать некоторый байт-код для машинного кода, который он может отправить в ЦП для выполнения напрямую.

В заключение, с компилируемыми языками, CPU запускает код напрямую. В интерпретируемых языках ЦП обычно запускает интерпретатор или виртуальную машину. Это делает интерпретируемые языки более медленными, чем скомпилированные языки, из-за накладных расходов на запуск виртуальной машины или интерпретатора.

ПРИМЕЧАНИЕ. Хотя мы говорим о интерпретируемых и скомпилированных языках, то, что мы действительно обсуждаем, является обычным стилем исполнения языка. PHP можно скомпилировать (используя HipHop), а C можно интерпретировать (используя Parrot VM).

Ответ 2

Хорошо, здесь много неправильных сообщений, время для долгого ответа.

Компилятор в основном понятен - он переводит программу из исходного языка на целевой язык. Оба языка могут быть любыми - язык высокого уровня, байт-код виртуальной машины, машинный код.

Интерпретатор, с другой стороны, не выполняет перевод, но непосредственно выполняет действия, предписанные конструкцией исходного языка, иначе интерпретирует его.

Рассмотрим гипотетическую инструкцию add в машине на основе стека, которая добавляет два верхних элемента стека и возвращает результат назад. Интерпретатор будет непосредственно выполнять это: "добавьте два верхних элемента и верните результат назад", аналогично:

switch (op)
{

 ....

  case OP_ADD:  
    op1 = pop (stack);
    op2 = pop (stack);
    res = op1 + op2;
    push (stack, res);
    break;

...
}

Как вы можете видеть, для одного add insn выполняется много операций: чтение и запись памяти, увеличение и уменьшение указателя стека, сама операция добавления, накладные расходы коммутатора (если интерпретатор реализован таким образом), накладные расходы цикла, который считывает каждый последующий insn и решает, как его обрабатывать и т.д.

Если интерпретатор работал в AST, это может выглядеть так:

swicth (op)
{
   ...
   case OP_ADD:
     op1 = tree->eval (left);
     op2 = tree->eval (right);
     return op1 + op2;
   ...
}

Опять же, многие, многие insns выполняют все, что требуется в семантике add.

Ответ 3

Компилятор переводит программу в машинный код перед запуском программы. Он решает все переменные, типы во время компиляции.

Интерпретатор обычно переводит каждый оператор в машинный код каждый раз, когда выполняется оператор.

Ответ 4

Граница между компиляцией и интерпретацией размыта. В общем, некоторые формы интерпретации работают медленнее, чем непосредственно скомпилированный код. Это может быть не обязательно верно в конкретных случаях.

Например, некоторые интерпретаторы непосредственно выполняют инструкции виртуальной машины (иногда переводятся в прямой или косвенный поток), что по очевидным причинам медленнее, чем собственный код.

Поскольку существует несоответствие импеданса между семантикой виртуальной машины (например, большинство из них основаны на стеках) и семантикой нативного кода, любой ad hoc перевод от одного к другому будет субоптимальным. Для выполнения супертяжелых оптимизаций вам понадобится виртуальная машина (или любая другая форма промежуточного представления) с определенным количеством целевой семантики целевой платформы в ней. LLVM - хороший пример такого представления.

Некоторые другие интерпретаторы даже оценивают код на уровне абстрактного синтаксического дерева. Некоторые из них выполняют замену строк (печально известный пример - Tcl).

Все эти методы легко реализовать, они предоставляют некоторые интересные динамические свойства семантике языка, но все они стоят за более медленное выполнение.

Еще одна важная вещь - суперкомпиляция. Этот метод практически превращает любого интерпретатора в компилятор, специализируя реализацию интерпретатора на конкретном экземпляре кода, который должен быть выполнен. Существование таких подходов делает разницу между компиляцией и интерпретацией еще более неопределенной.

Ответ 5

Согласно WikiPedia:

Интерпретация кода выполняется медленнее, чем запуск скомпилированного кода, потому что интерпретатор должен анализировать каждый оператор в программе каждый раз, когда он выполняется, а затем выполняет требуемое действие, тогда как скомпилированный код просто выполняет действие в фиксированном контексте, определяемом компиляцией. Этот анализ времени выполнения известен как "интерпретационные накладные расходы". Доступ к переменным также медленнее в интерпретаторе, потому что отображение идентификаторов в места хранения должно выполняться повторно во время выполнения, а не во время компиляции.

Ответ 6

Компилятор делает это один раз (это занимает некоторое время), тогда код работает быстро. Поскольку это может занять довольно долгое время, он может потратить некоторое время на оптимизацию кода.

Интерпретатор делает это, когда вы хотите запустить код, поэтому он компилируется каждый раз при его запуске.

Ответ 7

Интерпретатор выполняется по строкам и преобразует каждую строку в машинную инструкцию во время выполнения. В то время как компилятор преобразовал всю программу из исходного языка в целевой язык (скорее всего, машинную команду целевого процессора) во время компиляции.