Как можно использовать байт-код для оптимизации времени выполнения динамических языков? - PullRequest
4 голосов
/ 28 августа 2010

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

Ответы [ 2 ]

8 голосов
/ 28 августа 2010

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

Довольно обычный интерпретатор на основе AST будет выглядеть примерно так:

class ASTNode {
    virtual double execute() = 0;
}

class NumberNode {
    virtual double execute() { return m_value; }
    double m_value;
}

class AddNode {
    virtual double execute() { return left->execute() + right->execute(); }
}

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

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

while (1) {
    switch (op.type) {
        case op_add:
            // Efficient interpreters use "registers" rather than
            // a stack these days, but the example code would be more
            // complicated
            push(pop() + pop());
            continue;
        case op_end:
            return pop();
    }
}

Это все еще имеет достаточно высокую стоимость диспетчеризации по сравнению с собственным кодом, но намного быстрее, чем виртуальная диспетчеризация.Вы можете дополнительно улучшить производительность, используя расширение gcc, называемое «computed goto», которое позволяет удалить диспетчерскую диспетчеризацию, снижая общую стоимость диспетчеризации в основном до одной непрямой ветви.ряд дополнительных преимуществ перед интерпретаторами AST, в основном из-за способности байт-кода «напрямую» переходить в другие места, как на реальной машине, например, представить фрагмент кода, подобный следующему:

while (1) {
    ...statements...
    if (a)
        break;
    else
        continue;
}

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

while (condition->execute() == true) {
    for (i = 0; i < statements->length(); i++) {
        result = statements[i]->execute();
        if (result.type == BREAK)
            break;
        if (result.type == CONTINUE)
            i = 0;
    }
}

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

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

Наконец, интерпретатор AST по определению является рекурсивным и поэтому должен предотвращать переполнение системного стека, чтонакладывает очень жесткие ограничения на то, сколько вы можете использовать в своем коде, например:

1+(1+(1+(1+(1+(1+(1+(1+1)))))))

Имеет 8 уровней рекурсии (по крайней мере) в интерпретаторе - это может быть очень значительным расходом;более старые версии Safari (pre-SquirrelFish) использовали интерпретатор AST, и по этой причине JS было разрешено использовать только пару сотен уровней рекурсии по сравнению с 1000 в современных браузерах.

1 голос
/ 28 августа 2010

Возможно, вы могли бы взглянуть на различные методы, которые предоставляет инструмент llvm"opt".Это оптимизации от байт-кода к байт-коду, и сам инструмент предоставит анализ преимуществ применения определенной оптимизации.

...