Whi*_*ist 5 rakudo moarvm raku
也就是说,诸如$*scalar、@*array和 之类的变量%*hash。我问这个问题主要是因为我想了解它们对语法/正则表达式引擎的整体性能有多大负担。
为了使我的问题更加精确,我想知道会发生什么:
查找时,
my $var-1 = $*scalar
my $var-2 = @*array[3]
my $var-3 = %*hash<key>
Run Code Online (Sandbox Code Playgroud)
并在插入时,
$*scalar = "new value 1"
@*array[3] = "new value 2"
%*hash{"existing key"} = "new value 3"
%*hash{"new key"} = "new value 3"
Run Code Online (Sandbox Code Playgroud)
与符号表、调用堆栈或任何涉及的数据结构(例如用于处理动态作用域的特殊堆栈,或者可能是堆栈的哈希表)的关系。
我的主要兴趣是数组和哈希,以及每次我们推送/弹出/修改/插入数组元素或插入/修改/删除哈希的键/值对时它们是否完全重复。
编辑:
我对动态范围的假设是错误的。我认为,当修改动态范围的散列时,更改将在进行更改的词法范围的末尾被撤消,类似于 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;
Run Code Online (Sandbox Code Playgroud)
输出:
{key => value}
test() start
{key => value}
{key => new value}
test() end
{key => new value}
Run Code Online (Sandbox Code Playgroud)
珀尔
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;
Run Code Online (Sandbox Code Playgroud)
输出:
keyvalue
test() start
keyvalue
keynew value
test() end
keyvalue
Run Code Online (Sandbox Code Playgroud)
乐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;
Run Code Online (Sandbox Code Playgroud)
输出:
{key => value}
test() start
{key => value}
{key => new value}
test() end
{key => value}
Run Code Online (Sandbox Code Playgroud)
查找动态范围变量的成本与印记无关。出于查找的目的,印记只是要查找的变量名称的一部分。除了/的查找之外,@a[3] = $x和 的编译是相同的- 也就是说,它们都会导致对 的调用,传递已解析的变量、索引和要分配的值,从而导致分配。@*a[3] = $x@a@*apostcircumfix:<[ ]>
数组和散列被实现为可变数据结构;唯一会发生的重要复制是动态数组必须增长或者哈希表需要其后备存储增长以继续提供有效的查找。
抛开后备到GLOBAL和PROCESS,动态变量存储在词法符号表中(因此它们用 声明my)。sub在 MoarVM 中,术语“帧”用于调用堆栈上的记录,概念上 [1] 在or的入口处创建block并在其出口处销毁。术语“静态框架”用于表示在调用 asub或 时不变的所有事物的数据结构block。(因此,如果编写一个递归函数sub并调用它,则会有很多帧,但只有一个静态帧)。就词汇存储而言,静态帧包含一个将词汇变量名称(字符串)映射到整数的哈希表。词法的值作为数组存储在帧中,其索引由静态帧的哈希表映射。
大多数词法变量查找($a)完全绕过命名查找,在编译时解析为适用的索引。
相比之下,动态变量查找 ( $*a) 确实使用静态框架的查找表。动态变量查找通过遍历调用堆栈进行,在静态框架的词法散列中进行查找,以查看是否声明了这样的变量,如果是,则使用索引在框架中解析它。这里有一些通用机制和一种特殊情况机制可以提高性能。
通用的是:
$*a不必重复哈希;整数哈希码就在那里并准备好进行哈希表查找。特殊情况机制包括可以在任何帧上建立的动态变量查找缓存。这将名称与指向动态变量在调用堆栈上存储位置的指针一起存储。这提供了一条“快捷方式”,对于经常访问的动态变量尤其有价值,这些动态变量在调用堆栈中声明了大量帧。有关如何安装缓存条目的更多详细信息,请参阅此代码。(当进行延续时,它们在切片帧内部失效;它们过去在去优化期间也广泛失效,但随着大约一年前在堆栈展开上采用惰性去优化,情况发生了变化。)
[1] 许多在 MoarVM 上运行的 Raku 程序都具有相对较高的内联率,这消除了创建/销毁框架的成本。