Как динамические переменные реализованы в Rakudo/MoarVM?

То есть такие переменные, как$*scalar,@*arrayи%*hash. Я задаю этот вопрос главным образом потому, что хочу иметь представление о том, насколько сильно они влияют на общую производительность механизма грамматики/регулярных выражений.

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

при поиске,

      my $var-1 = $*scalar
my $var-2 = @*array[3]
my $var-3 = %*hash<key>

и при вставке,

      $*scalar = "new value 1"
@*array[3] = "new value 2"
%*hash{"existing key"} = "new value 3"
%*hash{"new key"}      = "new value 3"

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

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

РЕДАКТИРОВАТЬ: мои предположения о динамической области видимости были неверны. Я думал, что при изменении хэша с динамической областью действия изменения будут отменены в конце лексической области, в которой было сделано изменение, аналогично тому, что происходит в Perl сlocalключевое слово и связанный с ним «временный стек», следовательно, мой «Дублируется ли структура данных?» вопрос. Но это не то, что кажется динамическим.

Мое предположение заключалось в том, что следующий код Raku эквивалентен следующему коду Perl. Вместо этого, чтобы воспроизвести поведение кода Perl, мы должны сделать то, что делает вторая часть кода Raku.

Раку 1:

      my %*hash = (key => "value");

sub test() {
    say "test() start";
    say "\t", %*hash;
    %*hash<key>="new value";
    say "\t", %*hash;
    say "test() end";
}

say %*hash;
test();
say %*hash;

ВЫХОД:

      {key => value}
test() start
        {key => value}
        {key => new value}
test() end
{key => new value}

Перл

      our %hash=(key => "value");

sub test {
    local %hash=%hash;
    say "test() start";
    say "\t", %hash;
    $hash{key}="new value";
    say "\t", %hash;
    say "test() end"
}

say %hash;
test();
say %hash;

ВЫХОД:

      keyvalue
test() start
        keyvalue
        keynew value
test() end
keyvalue

Раку 2:

      my %*hash = (key => "value");

sub test() {
    my %*hash=CALLERS::<%*hash>;
    say "test() start";
    say "\t", %*hash;
    %*hash<key>="new value";
    say "\t", %*hash;
    say "test() end";
}

say %*hash;
test();
say %*hash;

ВЫХОД:

      {key => value}
test() start
        {key => value}
        {key => new value}
test() end
{key => value}

1 ответ

Затраты на поиск переменной с динамической областью действия не зависят от сигилы. Для целей поиска сигил — это просто часть имени переменной, которую нужно найти. Компиляция@a[3] = $xи@*a[3] = $xидентичны, за исключением поиска@a/@*a- то есть оба они приводят к вызовуpostcircumfix:<[ ]>, передавая разрешенную переменную, индекс и присваиваемое значение, что приводит к присваиванию.

Массивы и хэши реализованы как изменяемые структуры данных; единственное существенное копирование, которое может иметь место, — это если динамический массив должен расти или хеш-таблица нуждается в увеличении своего резервного хранилища, чтобы продолжать обеспечивать эффективный поиск.

Отложив запасной вариантGLOBALиPROCESS, динамические переменные хранятся в таблицах лексических символов (поэтому их объявление сmy). В MoarVM термин «фрейм» используется для записи в стеке вызовов, концептуально [1] созданной при входе в или и уничтоженной при его выходе. Термин «статический фрейм» используется для структуры данных, которая представляет все, что является инвариантным относительно вызовов илиblock. (Итак, если написать рекурсивноеsubи назовите его, кадров много, а статичный кадр всего один). Что касается лексического хранилища, статический фрейм содержит хеш-таблицу, отображающую имена лексических переменных (строки) в целые числа. Значения лексики хранятся во фрейме в виде массива с индексами, отображаемыми хэш-таблицей статического фрейма.

Большинство операций поиска лексических переменных ($a) полностью обходит именованный поиск, разрешаясь во время компиляции в применимый индекс.

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

Общие из них:

  • Хэш-коды строк кэшируются, поэтому строка$*aне придется повторно хэшировать; целочисленный хэш-код уже готов для поиска в хеш-таблице.
  • Строки интернируются внутри единицы компиляции, поэтому, если объявление и использование динамической переменной происходят в одном и том же файле, сравнение строк может привести к короткому замыканию при равенстве указателей.

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

[1] Многие программы Raku, работающие на MoarVM, имеют относительно высокую скорость встраивания, что устраняет затраты на создание/уничтожение фрейма.

Другие вопросы по тегам