Подтвердить что ты не робот

Почему интерпретируется (python | ruby)?

Каковы технические причины, по которым такие языки, как Python и Ruby, интерпретируются (из коробки) вместо компиляции? Мне кажется, что для людей, осведомленных в этой области, не должно быть слишком сложно заставить эти языки не интерпретироваться так, как они есть сегодня, и мы увидим значительный прирост производительности. Так что, конечно, я чего-то не хватает.

4b9b3361

Ответ 1

Несколько причин:

  • более быстрый цикл разработки, write-test vs write-compile-link-test
  • проще организовать динамическое поведение (отражение, метапрограммирование)
  • делает всю систему переносимой (просто перекомпилируйте базовый код C, и вы можете перейти на новую платформу).

Подумайте, что произойдет, если система не будет интерпретироваться. Скажем, вы использовали механизм перевода в C как механизм. Скомпилированный код периодически должен проверять, был ли он заменен метапрограммированием. Аналогичная ситуация возникает с функциями eval(). В таких случаях ему придется снова запустить компилятор, возмутительно медленный процесс или, во всяком случае, вовремя придется использовать интерпретатор.

Единственная альтернатива здесь - компилятор JIT. Эти системы очень сложны и сложны и имеют еще больший промежуток времени, чем все другие альтернативы. Они запускаются очень медленно, что делает их непрактичными для сценариев. Когда-либо видели Java script? Я этого не делал.

Итак, у вас есть два варианта:

  • все недостатки как компилятора, так и интерпретатора
  • просто недостатки интерпретатора

Не удивительно, что в основном первичная реализация идет со вторым выбором. Вполне возможно, что когда-нибудь мы увидим вторичные реализации, такие как компиляторы. Ruby 1.9 и Python имеют байт-коды VM; это и есть. Компилятор может ориентироваться только на нединамический код, или он может иметь различные уровни поддержки языка в качестве параметров. Но поскольку такая вещь не может быть основной реализацией, она представляет собой большую работу для очень незначительной выгоды. Ruby уже имеет 200 000 строк C в нем...

Я полагаю, я должен добавить, что всегда можно добавить скомпилированное расширение C (или, с некоторым усилием, любой другой язык). Итак, скажем, у вас медленная цифровая операция. Если вы добавите, скажем Array#newOp с реализацией C, то вы получите ускорение, программа останется в Ruby (или что-то еще), и ваша среда получит новый метод экземпляра. Все побеждают! Таким образом, это уменьшает потребность в проблемной вторичной реализации.

Ответ 2

Точно так же (в типичной реализации) Java или С#, Python сначала компилируется в какую-то форму байт-кода, в зависимости от реализации (CPython использует собственную специализированную форму, Jython использует JVM, как типичная Java, IronPython использует CLR, как обычный С#, и т.д.) - этот байт-код затем получает дополнительную обработку для выполнения виртуальной машиной (интерпретатором AKA), которая также может генерировать машинный код "как раз вовремя" - известный как JIT - если и когда это оправдано (реализация CLR и JVM часто выполняется, собственная виртуальная машина CPython обычно не может быть сделана для этого, например, с помощью psyco или Unladen Swallow).

JIT может оплачивать себя для достаточно длительных программ (если память дешевле, чем циклы CPU), но это может быть не так (из-за более медленного времени запуска и увеличения объема памяти), особенно когда типы также должны быть выведены или специализируется как часть генерации кода. Генерирование машинного кода без вывода типа или специализации легко, если это то, что вы хотите, например. freeze делает это для вас, но на самом деле не дает преимуществ, которые ему приписывают фетишисты машинного кода. Например, вы получаете исполняемый двоичный файл размером от 1,5 до 2 МБ вместо крошечного "мира привет" .pyc - не много смысла! -). Этот исполняемый файл является автономным и распространяется как таковой, но он будет работать только в очень узком диапазоне операционных систем и архитектур процессоров, поэтому компромиссы в большинстве случаев весьма неудобны. И время, необходимое для подготовки исполняемого файла, довольно продолжительное, поэтому было бы безумным выбором сделать этот режим работы по умолчанию.

Ответ 3

Простое замену интерпретатора компилятором не даст вам такого большого повышения производительности, как вы могли бы подумать о таком языке, как Python. Когда большинство времени фактически проводит символические поиски членов объекта в словарях, на самом деле не имеет значения, интерпретируется ли вызов функции, выполняющей такой поиск, или это собственный машинный код - разница, хотя и не совсем незначительная, будет затмевать накладные расходы.

Чтобы действительно повысить производительность, вам необходимо оптимизировать компиляторы. И методы оптимизации здесь сильно отличаются от того, что у вас есть с С++ или даже Java JIT - оптимизирующий компилятор для динамически типизированных/утиных языков, таких как Python, необходимо сделать очень креативный тип вывода (в том числе вероятностный - т.е. "вероятность 90% из них T", а затем генерирует эффективный машинный код для этого случая с проверкой/ветвью перед ним) и анализа escape. Это сложно.

Ответ 4

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

Ответ 5

Сегодня уже нет сильного различия между "скомпилированными" и "интерпретируемыми" языками. Python на самом деле скомпилирован так же сильно, как Java, единственные отличия:

  • Компилятор Python намного быстрее, чем компилятор Java
  • Python автоматически компилирует исходный код по мере его выполнения, нет отдельного шага "компиляции"
  • Байт-код Python отличается от байт-кода JVM

Python даже имеет функцию compile(), которая является интерфейсом к компилятору.

Похоже, различие, которое вы делаете, - это "динамически типизированные" и "статически типизированные" языки. В динамических языках, таких как Python, вы можете написать код, например:

def fn(x, y):
    return x.foo(y)

Обратите внимание, что типы x и y не указаны. Во время выполнения эта функция будет смотреть на x, чтобы увидеть, имеет ли она функцию-член с именем foo, и если это так будет вызывать ее с помощью y. Если нет, это вызовет ошибку времени выполнения, которая указывает, что такая функция не найдена. Такой вид просмотра во времени намного проще представлять с использованием промежуточного представления, такого как байт-код, где виртуальная среда выполнения выполняет поиск вместо того, чтобы генерировать машинный код для самого поиска (или вызвать функцию для поиска, которая является тем, что байт-код будет делать все равно).

Python имеет такие проекты, как Psyco, PyPy и Unladen Swallow, которые используют различные подходы к компиляции объектного кода Python в нечто более близкое к собственному коду. В этой области проводятся активные исследования, но пока нет (простого) ответа.

Ответ 6

Усилия, необходимые для создания хорошего компилятора для генерации собственного кода для нового языка, являются ошеломляющими. Небольшие исследовательские группы обычно занимают от 5 до 10 лет (примеры: SML/NJ, Haskell, Clean, Cecil, lcc, Objective Caml, MLton и многие другие). И когда соответствующий язык требует проверки типа и других решений, которые должны быть выполнены во время выполнения, писателю-компилятору приходится работать намного сложнее, чтобы получить хорошую производительность на собственном коду (отличный пример - см. Работу Крейга Чамберса и позднее Урса Хольцле Self). Вы можете надеяться, что производительность, которую вы можете надеяться, сложнее понять, чем вы думаете. Это явление частично объясняет почему интерпретируется так много динамически типизированных языков.

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

Наконец, хотя существуют быстрые компиляторы и медленные интерпретаторы, обычно проще ускорить цикл edit-translate-go с помощью интерпретатора. (Для некоторых хороших примеров быстрых компиляторов см. Вышеупомянутый lcc, а также Ken Thompson go. Пример сравнительно медленного интерпретатора см. в GHCi.

Ответ 7

Ну, разве не одна из сильных сторон этих языков, что они настолько легко доступны для сценариев? Они не были бы, если бы они были скомпилированы. И, с другой стороны, динамические языки легче вступать в процесс, чем компилировать.

Ответ 8

На скомпилированном языке цикл, в который вы попадаете при создании программного обеспечения, -

  • Внесите изменения
  • Скомпилировать изменения
  • Тестовые изменения
  • goto 1

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

Это не обязательно причина, о которой думали дизайнеры python | Ruby, но имейте в виду, что "насколько эффективно машина запускает это?" это лишь половина проблемы разработки программного обеспечения.

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

Ответ 9

REPL. Не стучите, пока не попробуете.:)

Ответ 10

По дизайну.

Авторы хотели, чтобы они могли писать сценарии.

Python скомпилируется при первом запуске, хотя

Ответ 11

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

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

Хорошей новостью является то, что есть способы преодоления этого. Self, Smalltalk и Lisp/Scheme успешно справились с большинством тех же проблем. Но для этого нужно время, чтобы просеять его и выяснить, как заставить его работать с Ruby. Это также не помогает тому, что Ruby имеет очень запутанную грамматику.

Ответ 12

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

Учитывая это, и что почти единственные преимущества компилятора - проверка типов (сложность в динамическом языке) и скорость, нет большого стимула писать компиляторы для большинства интерпретируемых языков.