2009-11-26 2 views
18

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

+2

Тот факт, что (по крайней мере, у Python) было более одного проекта компилятора, который длится годами, и что он все еще не совсем там, должен отбросить ваши ожидания от того, «не слишком сложно», я думаю. –

+5

На самом деле, я думаю, что Python компилируется в код виртуальной машины, аналогичный .NET и Java. В любом случае, я думаю, почему языковые разработчики делают выбор интерпретации или компиляции, это хороший вопрос. Вы можете проверить http://stackoverflow.com/questions/475223/what-is-the-difference-between-implementing-a-compiler-and-an-interpreter –

+2

Вам также может быть интересен этот ответ: http : //stackoverflow.com/questions/376611/why-interpreted-langs-are-mostly-ducktyped-while-compiled-have-strong-typing/376828#376828 –

ответ

1

По дизайну.

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

Python компилируется первый раз, когда он выполняется, хотя

+0

т. е. «Байт скомпилирован» (в .pyc) в первый раз он выполняется. Это только ускоряет время загрузки будущих исполнений. Я думаю, что искатель говорит о том, что он скомпилирован в собственный машинный код. – overthink

+0

Perl тоже ... вздох, нет тела спрашивает о Perl. – dlamblin

+1

«Авторы хотели, чтобы они могли писать сценарии».? Это заявление смущает меня. Пригодность цели для заданных заданий данного языка имеет гораздо меньше общего с циклом write/(компиляция, связь)/run, чем богатство стандартной библиотеки, управление памятью и модель типа. Это, вероятно, также обеспечит некоторую поддержку для написания скриптов в Java, если не было другого небольшого рассмотрения того, сколько накладных расходов требуется для запуска виртуальной машины ... –

2

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

6

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

+0

Я не уверен, что аргумент переносимости звучит. Если вы отделите свой lexer/AST-builder от своего кода генерации кода, вы можете изменить эти два независимо (что несколько, как gcc архивируется). Поэтому я не вижу в этом причины. Ваша мысль о том, что система динамического типа проще реализовать на интерпретируемом языке, звучит. Фактически, любая система динамического типа требует поддержки по времени выполнения (либо через интерпретатора, либо через библиотеку времени исполнения) практически по определению. –

+0

Аргумент переносимости на самом деле вовсе не звучит. Обычно интерпретируемые языки легче переносить, чем скомпилированные. Единственная причина, по которой они могут показаться * проще переносить, заключается в том, что вы можете написать свой интерпретированный языковой порт, не понимая основную машину, но это всего лишь вопрос компетентности, а не трудности. –

+0

вы, ребята, правы .. это вышло не так! Я собираюсь обновить свой ответ, чтобы сказать, о чем я действительно думал. – neesh

2

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

  1. Внести изменения
  2. Compile изменения
  3. Тест изменения
  4. GOTO 1

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

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

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

32

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

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

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

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

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

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

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

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

+5

«Когда-либо видел сценарий Java? У меня нет». Ха! Таким образом, ваши очки репутации могут быть в 30 раз больше, чем мои, но даже я слышал о JavaScript! – mtyaka

+4

Хе-хе, это хорошо. На самом деле, я многому учусь, и я знаю, что мне еще многое предстоит узнать. Но, несмотря на английский-каламбур, (1) «сценарием» я имел в виду «исполняемый оболочкой сценарий, например, в bash или perl»; (2) Я никогда не видел ни одного из них в JS, и (3) JS практически не имеет никакого отношения к Java. – DigitalRoss

+3

@mtyaka: Ясно, что он означает «скрипт, написанный на Java», а не «Javascript», который является совершенно другим языком. @DigitalRoss: на самом деле, javascript набирает популярность в качестве не-веб-скриптового языка с появлением автономных переводчиков, таких как V8 и SquirrelFish. –

5

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

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

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

Это звучит так, как различие, которое вы делаете, находится между «динамически типизированными» и «статически типизированными» языками. В динамических языков, таких как Python, вы можете написать код, как:

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

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

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

16

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

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

8

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

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

5

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

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

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

2

REPL. Не стучите его «, пока Вы не пробовали. :)

+1

SML/NJ предлагает скомпилированный код REPL уже более 20 лет ... как и многие системы Lisp. –

1

Компиляция Ruby по крайней мере, как известно, тяжелая. Я работаю над одним, и в рамках этого я написал сообщение в блоге enumerating some of the issues here.

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

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

1

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

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

Смежные вопросы