Скомпилированные и интерпретированные языки

284

Я пытаюсь лучше понять разницу. В Интернете я нашел много объяснений, но они имеют тенденцию к абстрактным различиям, а не к практическим последствиям.

Большая часть моего опыта программирования была с CPython (динамический, интерпретируемый) и Java (статический, скомпилированный). Однако я понимаю, что существуют другие виды интерпретируемых и компилируемых языков. Помимо того, что исполняемые файлы могут распространяться из программ, написанных на скомпилированных языках, есть ли какие-либо преимущества / недостатки для каждого типа? Часто я слышу, как люди утверждают, что интерпретируемые языки могут использоваться в интерактивном режиме, но я считаю, что скомпилированные языки также могут иметь интерактивные реализации, верно?

chimeracoder
источник
32
Вы выбрали именно худшие языки для этого сравнения. Оба компилируются. Единственная реальная разница между ними - это JITer, и даже Python имеет частичную (psyco).
Игнасио Васкес-Абрамс
1
Хорошим примером интерактивного скомпилированного языка является Clojure - все полностью скомпилировано (сначала в JVM, а затем в нативный код через JIT). Однако большая часть перекомпиляции происходит динамически, и разработка часто выполняется в интерактивной оболочке REPL, где вы можете оценить любую функцию, которую вы хотите в рабочей среде.
Микера
Standard ML - еще один интерактивный скомпилированный язык; встроенный компилятор также выдает настоящий машинный код.
Донал Феллоуз
queception.com/question.php?question=16
Переполнение стека

Ответы:

460

Скомпилированный язык - это язык, в котором программа после компиляции выражается в инструкциях целевой машины. Например, операция «+» в вашем исходном коде может быть переведена непосредственно в инструкцию «ДОБАВИТЬ» в машинном коде.

Интерпретируемый язык является одним где инструкции непосредственно не выполняется на целевой машине, но вместо того, чтобы прочитать и выполнить какой - либо другой программы (которая обычно является написанной на языке родной машины). Например, та же самая операция «+» будет распознаваться интерпретатором во время выполнения, который затем будет вызывать свою собственную функцию «add (a, b)» с соответствующими аргументами, которая затем будет выполнять инструкцию «ADD» машинного кода. ,

Вы можете делать все, что можете на интерпретируемом языке, на скомпилированном языке и наоборот - оба они выполнены по Тьюрингу. Оба имеют свои преимущества и недостатки для реализации и использования.

Я собираюсь полностью обобщить (пуристы, прости меня!), Но, примерно, вот преимущества компилируемых языков:

  • Повышение производительности благодаря непосредственному использованию собственного кода целевой машины
  • Возможность применять довольно мощные оптимизации на этапе компиляции

И вот преимущества интерпретируемых языков:

  • Проще реализовать (писать хорошие компиляторы очень сложно !!)
  • Нет необходимости запускать этап компиляции: можно выполнять код напрямую «на лету»
  • Может быть более удобным для динамических языков

Обратите внимание, что современные методы, такие как компиляция байт-кода, добавляют дополнительную сложность - здесь происходит то, что компилятор нацелен на «виртуальную машину», которая отличается от базового оборудования. Эти инструкции виртуальной машины могут быть затем скомпилированы снова на более позднем этапе, чтобы получить собственный код (например, как это делается JIT-компилятором Java JVM).

mikera
источник
1
Не все компилируемые языки нуждаются в медленной стадии компиляции. Серьезные реализации Common Lisp являются компиляторами, и они часто не связываются с интерпретатором, предпочитая просто быстро компилировать на лету. С другой стороны, Java требует этапа компиляции, и он обычно виден.
Дэвид Торнли
2
@Kareem: JIT-компилятор выполняет только 1) и 2) один раз - после этого он полностью является нативным кодом. Интерпретатор должен выполнять и 1) и 2) каждый раз, когда вызывается код (что может быть много, много раз ...). Так что со временем JIT-компилятор побеждает с большим отрывом.
Микера
3
Да, байт-код переводится в машинный код в какой-то момент в течение всего выполнения программы (в отличие от выполнения программы до, как в случае с традиционным компилятором). Но данный фрагмент кода может быть выполнен более 10 миллионов раз за время выполнения программы. Он (вероятно) компилируется только один раз из байт-кода в машинный код. Следовательно, накладные расходы времени выполнения JIT невелики и могут быть проигнорированы для долго работающих программ. После того, как JIT-компилятор завершит свою работу, вы будете эффективно выполнять чистый машинный код.
Микера
2
Это на самом деле ложная дихотомия. Нет ничего присущего языку, который делает его скомпилированным для нашего толкования. Это не более чем широко распространенное заблуждение. У многих языков есть обе реализации, и у всех языков может быть любой.
Ммаченри
2
@ Mmachenry это не ложная дихотомия. «Язык программирования» включает в себя как дизайн, так и реализацию. Хотя в теоретическом смысле данное определение языка может быть скомпилировано и интерпретировано, в реальной практике существуют значительные различия в реализации. Например, еще никто не решил, как эффективно компилировать определенные языковые конструкции - это проблема открытых исследований.
Микера
99

Сам язык не компилируется и не интерпретируется, есть только конкретная реализация языка. Ява - прекрасный пример. Существует платформа на основе байт-кода (JVM), собственный компилятор (gcj) и интерпретатор для расширенного набора Java (bsh). Так что же такое Java сейчас? Байт-код скомпилирован, нативно скомпилирован или интерпретирован?

Другими языками, которые составляются и интерпретируются, являются Scala, Haskell или Ocaml. Каждый из этих языков имеет интерактивный интерпретатор, а также компилятор для байт-кода или машинного кода.

Поэтому классификация языков по «скомпилированным» и «интерпретированным» не имеет особого смысла.

lunaryorn
источник
3
Я согласен. Или, скажем, есть нативные компиляторы (создающие машинный код для процессора) и не очень нативные компиляторы (создающие токенизированные вещи, то есть промежуточный код, которые какой-то своевременный компилятор компилирует в машинный код раньше ( или во время) runtime ONCE), и существуют «настоящие» некомпиляторы, которые никогда не генерируют машинный код и никогда не позволяют процессору запускать код. Последние являются переводчиками. Сегодня нативные компиляторы, которые напрямую генерируют машинный (CPU) код во время компиляции, становятся все более редкими. Delphi / Codegear - один из лучших выживших.
TheBlastOne
57

Начните думать с точки зрения: взрыва из прошлого

Когда-то давным-давно в стране жили вычислительные интерпретаторы и компиляторы. Все виды суеты возникли из-за заслуг одного над другим. Общее мнение в то время было чем-то вроде:

  • Интерпретатор: Быстрая разработка (редактирование и запуск). Медленно выполняется, потому что каждый оператор должен интерпретироваться в машинный код каждый раз, когда он выполняется (подумайте, что это значило для цикла, выполняемого тысячи раз).
  • Компилятор: медленно развивается (редактировать, компилировать, связывать и запускать. Шаги компиляции / линковки могут занять много времени). Быстро выполнить. Вся программа была уже на родном машинном коде.

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

Сегодня ландшафт развился до такой степени, что скомпилированное / интерпретированное различие в значительной степени не имеет значения. Многие скомпилированные языки обращаются к сервисам времени выполнения, которые не полностью основаны на машинном коде. Кроме того, большинство интерпретируемых языков «компилируются» в байт-код перед выполнением. Интерпретаторы байт-кода могут быть очень эффективными и конкурировать с некоторым кодом, сгенерированным компилятором, с точки зрения скорости выполнения.

Классическое отличие состоит в том, что компиляторы генерируют собственный машинный код, интерпретаторы читают исходный код и генерируют машинный код на лету, используя какую-то систему времени выполнения. Сегодня осталось очень мало классических интерпретаторов - почти все они компилируются в байт-код (или в другое полускомпилированное состояние), который затем запускается на виртуальной «машине».

NealB
источник
1
Хорошо - отличное резюме в последнем абзаце - спасибо!
ckib16
26

Крайние и простые случаи:

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

  • Интерпретатор предоставит пользователю подсказку в цикле, где он может вводить операторы или код, и при нажатии RUNили на эквивалент интерпретатор интерпретирует, сканирует, анализирует и интерпретативно выполняет каждую строку, пока программа не достигнет точки остановки или ошибки , Поскольку каждая строка обрабатывается сама по себе, и интерпретатор ничего не «изучает» из увиденного ранее строки, каждый раз для каждой строки предпринимается попытка преобразования понятного человеку языка в машинные инструкции, так что это медлительно. С другой стороны, пользователь может проверять и иным образом взаимодействовать со своей программой различными способами: изменение переменных, изменение кода, запуск в режимах трассировки или отладки ... что угодно.

Позвольте мне объяснить, что жизнь уже не так проста. Например,

  • Многие интерпретаторы предварительно компилируют код, который они дают, поэтому шаг перевода не нужно повторять снова и снова.
  • Некоторые компиляторы компилируют не машинные инструкции для конкретного процессора, а байт-код, своего рода искусственный машинный код для вымышленной машины. Это делает скомпилированную программу более переносимой, но требует интерпретатора байт-кода в каждой целевой системе.
  • Интерпретаторы байт-кода (я смотрю на Java здесь) в последнее время имеют тенденцию перекомпилировать байт-код, который они получают для ЦП целевой секции непосредственно перед выполнением (называемым JIT). Чтобы сэкономить время, это часто делается только для кода, который часто выполняется (горячие точки).
  • Некоторые системы, которые выглядят и действуют как интерпретаторы (например, Clojure), компилируют любой код, который они получают, немедленно, но обеспечивают интерактивный доступ к программной среде. Это в основном удобство интерпретаторов со скоростью двоичной компиляции.
  • Некоторые компиляторы на самом деле не компилируются, они просто предварительно переваривают и сжимают код. Некоторое время назад я слышал, как работает Perl. Так что иногда компилятор просто выполняет небольшую часть работы, и большая часть этого все еще остается интерпретацией.

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

Карл Смотриц
источник
23

С http://www.quora.com/What-is-the-difference-between-compiled-and-interpreted-programming-languages

Разницы нет, потому что «скомпилированный язык программирования» и «интерпретируемый язык программирования» не являются осмысленными понятиями. Любой язык программирования, и я имею в виду любой, может быть интерпретирован или скомпилирован. Таким образом, интерпретация и компиляция - это методы реализации, а не атрибуты языков.

Интерпретация - это метод, при котором другая программа, интерпретатор, выполняет операции от имени интерпретируемой программы для ее запуска. Если вы можете вообразить, что читаете программу и делаете то, что она говорит, шаг за шагом, скажем, на бумаге, это то, что делает переводчик. Распространенной причиной для интерпретации программы является то, что интерпретаторы пишутся относительно легко. Другая причина заключается в том, что интерпретатор может отслеживать, что программа пытается сделать во время работы, чтобы обеспечить соблюдение политики, скажем, безопасности.

Компиляция - это метод, при котором программа, написанная на одном языке («исходный язык»), переводится в программу на другом языке («объектный язык»), что, как мы надеемся, означает то же самое, что и исходная программа. При выполнении перевода, как правило, компилятор также пытается преобразовать программу таким образом, чтобы сделать объектную программу быстрее (без изменения ее значения!). Распространенная причина для компиляции программы заключается в том, что есть хороший способ быстро запускать программы на объектном языке без дополнительных затрат на интерпретацию исходного языка.

Исходя из приведенных выше определений, вы, возможно, догадались, что эти два метода реализации не являются взаимоисключающими и даже могут быть взаимодополняющими. Традиционно, объектным языком компилятора был машинный код или что-то подобное, что относится к любому количеству языков программирования, понимаемых конкретными процессорами компьютера. Затем машинный код запускается «на металле» (хотя, если присмотреться, можно увидеть, что «металл» работает во многом как интерпретатор). Однако сегодня очень распространено использование компилятора для генерации объектного кода, который должен интерпретироваться - например, именно так Java работала (а иногда и до сих пор) работает. Есть компиляторы, которые переводят другие языки в JavaScript, который затем часто запускают в веб-браузере, который может интерпретировать JavaScript, или скомпилируйте виртуальную машину или собственный код. У нас также есть интерпретаторы машинного кода, которые можно использовать для эмуляции одного типа оборудования на другом. Или можно использовать компилятор для генерации объектного кода, который затем является исходным кодом для другого компилятора, который может даже скомпилировать код в памяти как раз вовремя для его запуска, что в свою очередь. , , Вы поняли идею. Есть много способов объединить эти понятия.

Бхавин шах
источник
Можете ли вы исправить это предложение: «Существуют компиляторы, которые переводят другие языки в JavaScript, который затем часто запускается в веб-браузере, который может интерпретировать JavaScript или компилировать его на виртуальной машине или в собственном коде».
Корай Тугай
Успешно справился. Другая распространенная ошибка - приписывать полезность языка существующим API.
Little Endian
10

Самое большое преимущество интерпретируемого исходного кода перед скомпилированным исходным кодом - это ПОРТАТИВНОСТЬ .

Если ваш исходный код скомпилирован, вам нужно скомпилировать разные исполняемые файлы для каждого типа процессора и / или платформы, на которых вы хотите, чтобы ваша программа работала (например, один для Windows x86, один для Windows x64, один для Linux x64 и т. Д. на). Кроме того, если ваш код не полностью совместим со стандартами и не использует каких-либо платформо-зависимых функций / библиотек, вам фактически потребуется написать и поддерживать несколько баз кода!

Если ваш исходный код интерпретируется, вам нужно написать его только один раз, и он может быть интерпретирован и выполнен соответствующим интерпретатором на любой платформе! Это портативный ! Обратите внимание , что сам интерпретатор исполняемая программа , которая будет написана и составлена для конкретной платформы.

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

Нико Беллик
источник
1
на этих условиях java нельзя считать «компилируемым языком», но его фаза компиляции дает преимущества компиляции (проверка типов, раннее обнаружение ошибок и т. д.) и создает байт-код, который можно запускать на любой ОС с помощью Java. виртуальная машина предоставляется.
Рохелио Тривиньо
7

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

Традиционно «скомпилированный» означает, что этот перевод выполняется за один раз, выполняется разработчиком, а полученный исполняемый файл распространяется среди пользователей. Чистый пример: C ++. Компиляция обычно занимает довольно много времени и пытается выполнить много дорогостоящей оптимизации, чтобы результирующий исполняемый файл работал быстрее. Конечные пользователи не имеют инструментов и знаний для самостоятельной компиляции, а исполняемый файл часто должен работать на различном оборудовании, поэтому вы не можете выполнять многие аппаратные оптимизации. Во время разработки отдельный этап компиляции означает более длительный цикл обратной связи.

Традиционно «интерпретированный» означает, что перевод происходит «на лету», когда пользователь хочет запустить программу. Чистый пример: ванильный PHP. Наивный интерпретатор должен анализировать и переводить каждый фрагмент кода при каждом запуске, что делает его очень медленным. Он не может выполнять сложные, дорогостоящие оптимизации, поскольку они занимают больше времени, чем время, сэкономленное при выполнении. Но он может полностью использовать возможности оборудования, на котором он работает. Отсутствие отдельного этапа компиляции сокращает время обратной связи во время разработки.

Но в настоящее время «скомпилировано и интерпретировано» - это не черно-белая проблема, между ними есть оттенки. Наивные, простые интерпретаторы в значительной степени вымерли. Во многих языках используется двухэтапный процесс, когда высокоуровневый код транслируется в независимый от платформы байт-код (который интерпретируется гораздо быстрее). Кроме того, у вас есть «своевременные компиляторы», которые компилируют код не чаще одного раза за запуск программы, иногда кешируют результаты и даже разумно решают интерпретировать код, который выполняется редко, и проводят мощную оптимизацию для кода, который часто выполняется. Во время разработки отладчики способны переключать код внутри работающей программы даже для традиционно скомпилированных языков.

Майкл Боргвардт
источник
1
Тем не менее, модель компиляции C ++ унаследована от C и была разработана без учета таких функций, как шаблоны. Эта неловкость значительно увеличивает время компиляции C ++, чем любой другой фактор, и делает ее плохим примером.
4

Во-первых, пояснение: Java не полностью статически скомпилирована и связана как C ++. Он компилируется в байт-код, который затем интерпретируется JVM. JVM может выполнять компиляцию точно в срок на родном машинном языке, но это не обязательно.

Более того: я думаю, что интерактивность - это главное практическое отличие. Поскольку все интерпретируется, вы можете взять небольшой фрагмент кода, проанализировать и запустить его в соответствии с текущим состоянием среды. Таким образом, если бы вы уже выполнили код, который инициализировал переменную, у вас был бы доступ к этой переменной и т. Д. Это действительно подходит для таких вещей, как функциональный стиль.

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

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

Что касается генерации исполняемых файлов, это не имеет к этому никакого отношения, ИМХО. Вы часто можете создать исполняемый файл из скомпилированного языка. Но вы также можете создать исполняемый файл из интерпретируемого языка, за исключением того, что интерпретатор и среда выполнения уже упакованы в исполняемый и скрыты от вас. Это означает, что вы, как правило, по-прежнему оплачиваете затраты времени выполнения (хотя я уверен, что для некоторых языков есть способы перевести все в исполняемый файл дерева).

Я не согласен с тем, что все языки можно сделать интерактивными. Некоторые языки, такие как C, настолько привязаны к машине и всей структуре ссылок, что я не уверен, что вы сможете создать полноценную полноценную интерактивную версию

Uri
источник
С на самом деле не привязан к "машине". Синтаксис и семантика C довольно просты. Не должно быть особенно сложно реализовать C-интерпретатор, только очень трудоемкий (потому что также должна быть реализована стандартная библиотека). И, между прочим, Java может быть скомпилирована в машинный код (используя gcj).
lunaryorn
@lunaryorn: я не согласен с GCJ. GCJ просто дает вам исполняемую среду. «Скомпилированные приложения связаны со средой выполнения GCJ libgcj, которая предоставляет библиотеки базовых классов, сборщик мусора и интерпретатор байт-кода»
Ури
2
GCJ делает нативный машинный код, а не только исполняемую среду с встроенным интерпретатором и байткодом. libgcj предоставляет интерпретатор байт-кода для поддержки вызовов из нативного кода в байт-код Java, а не для интерпретации скомпилированной программы. Если libgcj не предоставляет интерпретатор байт-кода, GCJ не будет соответствовать спецификации Java.
lunaryorn
@lunaryorn: Ах. Хорошо, я ценю разъяснения и исправлюсь. Мы в основном используем Java в среде Windows, поэтому я не пробовал gcj годами.
Ури
2

Практически сложно дать практический ответ, потому что разница заключается в самом определении языка. Можно создать интерпретатор для каждого скомпилированного языка, но невозможно создать компилятор для каждого интерпретируемого языка. Это очень много о формальном определении языка. Так что теоретической информатике Нобобы нравится в университете.

Стивен Мор
источник
1
Конечно, вы можете создать компилятор для интерпретируемого языка, но скомпилированный машинный код сам по себе является зеркалом времени выполнения.
Эйден Белл
2

Книга Python © 2015 Imagine Publishing Ltd, просто расстраивает разницу следующим указанием, упомянутым на странице 10, как:

Интерпретируемый язык, такой как Python, - это язык, в котором исходный код преобразуется в машинный код, а затем выполняется при каждом запуске программы. Это отличается от скомпилированного языка, такого как C, где исходный код преобразуется в машинный код только один раз - полученный машинный код затем выполняется при каждом запуске программы.

Ахмед Шабан Хельва
источник
1

Компиляция - это процесс создания исполняемой программы из кода, написанного на скомпилированном языке программирования. Компиляция позволяет компьютеру запускать и понимать программу без необходимости использования программного обеспечения для ее создания. Когда программа компилируется, она часто компилируется для конкретной платформы (например, платформа IBM), которая работает с IBM-совместимыми компьютерами, но не с другими платформами (например, платформа Apple). Первый компилятор был разработан Грейс Хоппер во время работы на компьютере Harvard Mark I. Сегодня большинство языков высокого уровня будут включать свой собственный компилятор или иметь в наличии наборы инструментов, которые можно использовать для компиляции программы. Хорошим примером компилятора, используемого с Java, является Eclipse, а примером компилятора, используемого с C и C ++, является команда gcc.

salehvm
источник
0

Краткое (неточное) определение:

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

Интерпретируемый язык: Программа читается построчно, и как только строка читается, машинные инструкции для этой строки выполняются ЦПУ.

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

В чем разница между компиляцией и интерпретацией?

Или мой более поздний пост в блоге:

https://orangejuiceliberationfront.com/the-difference-between-compiler-and-interpreter/

uliwitness
источник