Переводчики: сколько упрощений? - PullRequest
2 голосов
/ 11 мая 2010

В моем интерпретаторе код, подобный следующему

x=(y+4)*z
echo x

анализирует и "оптимизирует" до четырех отдельных операций, выполняемых интерпретатором, в значительной степени подобно сборке:

add 4 to y
multiply <last operation result> with z
set x to <last operation result>
echo x
  • В современных интерпретаторах (например, CPython, Ruby, PHP) насколько упрощены "коды операций", для которых интерпретатор выполняет конечный эффект?

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

Ответы [ 4 ]

1 голос
/ 11 мая 2010
  1. О последнем результате: фактически вы создали регистрационную машину с одним регистром: «результат последней операции». Который является блокиратором параллелизма.
  2. Опкоды типа Eval / assign обычно на уровень ниже, чем ваш. Взгляните на коды операций Python .
  3. Команды более высокого уровня могут повысить производительность, поскольку они позволяют вам проводить больше времени (надеюсь) в быстром интерпретаторе. Но они также могут причинять боль, потому что вам понадобится другой высокоуровневый код операции для этого и того.

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

  • структурированное программирование. Я имею в виду, будут ли коды операций содержать понятия «вызов функции», «вызов функции с N аргументами» или это будет последовательность «push, push, push, call, ret».
  • лямбды (в частности - функциональные тела, определенные внутри других функций). Что поставит решение замыкание : должны ли функции «захватывать» переменные вне области видимости и, если да, то как.
1 голос
/ 11 мая 2010

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

1 голос
/ 11 мая 2010

В случае с Python вы можете указать байт-код для данной функции с модулем dis .

from dis import dis
def foo():
    x=(y+4)*z
    print x

dis(foo)

дает вам:

2           0 LOAD_GLOBAL              0 (y)
            3 LOAD_CONST               1 (4)
            6 BINARY_ADD          
            7 LOAD_GLOBAL              1 (z)
           10 BINARY_MULTIPLY     
           11 STORE_FAST               0 (x)

3          14 LOAD_FAST                0 (x)
           17 PRINT_ITEM          
           18 PRINT_NEWLINE       
           19 LOAD_CONST               0 (None)
           22 RETURN_VALUE        

Некоторое из этого является посторонним (например, LOAD_CONST и RETURN_VALUE в конце предназначены для неявного return None в foo()), но Python, кажется, помещает y и 4 в стек, добавляет, нажимает z, умножает и написать в х. Затем он нажимает х и печатает

0 голосов
/ 12 мая 2010

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

В любом случае, в наши дни, когда есть все, что есть .NET, JVM, LLVM, действительно дешево и легко подключить подходящий JIT-компилятор, если вы действительно заинтересованы в производительности вашего интерпретатора.

...