Почему (python | ruby) интерпретируется? - PullRequest
18 голосов
/ 26 ноября 2009

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

Ответы [ 12 ]

32 голосов
/ 26 ноября 2009

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

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

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

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

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

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

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

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

16 голосов
/ 26 ноября 2009

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

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

8 голосов
/ 27 ноября 2009

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

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

6 голосов
/ 26 ноября 2009

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

5 голосов
/ 27 ноября 2009

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

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

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

5 голосов
/ 26 ноября 2009

Сегодня больше нет четкого различия между «скомпилированными» и «интерпретируемыми» языками. 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 во что-то более похожее на нативный код. В этой области ведутся активные исследования, но пока нет простого ответа.

2 голосов
/ 27 ноября 2009

РЕПЛ . Не стучите, пока не попробуете. :)

2 голосов
/ 26 ноября 2009

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

  1. Внести изменения
  2. Изменения компиляции
  3. Тестовые изменения
  4. Перейти к 1

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

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

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

2 голосов
/ 26 ноября 2009

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

1 голос
/ 18 декабря 2009

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

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

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