Как динамические переменные реализованы в 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, имеют относительно высокую скорость встраивания, что устраняет затраты на создание/уничтожение фрейма.