首先,这是不同类型的列表for
循环和可以应用的优化。所有这些都存在于 Perl 从 5.6 到 5.20(现在)的每个版本中,我相信它是全面的。
-
for (EXPR; EXPR; EXPR)
⇒“C 风格的 for 循环”,一个增强的 while 循环。
-
for (EXPRX..EXPRY)
⇒ 将一个范围而不是其他内容优化为计数循环。
-
for (@ARRAY)
⇒ 直接访问数组而不是展平其他任何东西。
-
for (reverse LIST)
⇒ 上述优化均不适用,但列表是反向遍历的,而不是反向的。
-
for (LIST)
⇒ 在通用的 foreach 循环中,LIST
表达式在循环开始之前计算。
When CONSTX..CONSTY
被展平(即除for (CONSTX..CONSTY)
),它在编译时而不是运行时被展平。
黑盒证明
基线内存使用情况:
$ perl -e'system(ps, ho, rss, 0+$$);'
1540 # 1.5 MiB
一般情况趋于平缓。
$ perl -e'$y=2_000_000; for ((),1..$y) { system(ps, ho, rss, 0+$$); last }'
80208 # 78 MiB
或者更糟。 (除了正常的堆栈使用之外,它还在编译时展平为数组。)
$ perl -e'for ((),1..2_000_000) { system(ps, ho, rss, 0+$$); last }'
143224 # 140 MiB
for (CONST..CONST)
不平坦。
$ perl -e'for (1..2_000_000) { system(ps, ho, rss, 0+$$); last }'
1540 # 1.5 MiB
实际上,for (EXPR..EXPR)
一般不会变平。
$ perl -e'$y=2_000_000; for (1..$y) { system(ps, ho, rss, 0+$$); last }'
1540 # 1.5 MiB
即使没有工具,您也可以看出编译时间的差异。
$ time perl -c -e'1 for 1..2_000_000'
-e syntax OK
real 0m0.010s
user 0m0.004s
sys 0m0.000s
$ time perl -c -e'1 for (),1..2_000_000'
-e syntax OK
real 0m1.197s
user 0m0.952s
sys 0m0.232s
白盒证明
未优化的情况使用范围运算符l它的上下文。内存中的完整列表。
$ perl -MO=Concise,-exec -e'$y=1_000_000; 1 for (),1..$y;'
...
8 <|> range(other->9)[t3] lK/1 <-- Range operator
9 <#> gvsv[*y] s
a <1> flop lKM
goto b
i <$> const[IV 1] s
j <1> flip[t4] lK/LINENUM
b <#> gv[*_] s
c <{> enteriter(next->d last->g redo->d) lK/8 <-- No S
...
这是编译时展平的范围的样子:
$ perl -MO=Concise,-exec -e'1 for (),1..1_000_000;'
...
4 <$> const[AV ] s <-- Constant array
5 <1> rv2av lKPM/1
6 <#> gv[*_] s
7 <{> enteriter(next->8 last->b redo->8) lK/8 <-- No S
...
你可以看到for (CONST..CONST)
创建一个enteriter
带有“S”标志。在enteriter
,“S”标志表示它是一个计数循环。
$ perl -MO=Concise,-exec -e'1 for 1..1_000_000;'
...
4 <$> const[IV 1] s
5 <$> const[IV 1000000] s
6 <#> gv[*_] s
7 <{> enteriter(next->8 last->b redo->8) lKS/8 <-- S
...
同样适用于for (EXPR..EXPR)
一般来说。
$ perl -MO=Concise,-exec -e'$y=1_000_000; 1 for 1..$y;'
...
8 <$> const[IV 1] s
9 <#> gvsv[*y] s
a <#> gv[*_] s
b <{> enteriter(next->c last->f redo->c) lKS/8 <-- S
...
Even for (@a)
没有被压扁!
$ perl -MO=Concise,-exec -e'1 for @a;'
...
4 <#> gv[*a] s
5 <1> rv2av[t2] sKRM/1
6 <#> gv[*_] s
7 <{> enteriter(next->8 last->b redo->8) lKS/8 <-- S
...
再检查一遍
$ perl -MO=Concise,-exec -e'1 for (),@a;'
...
4 <#> gv[*a] s
5 <1> rv2av[t2] lKM/1
6 <#> gv[*_] s
7 <{> enteriter(next->8 last->b redo->8) lK/8 <-- No S
...
查找“S”标志的代码将确认所有这些。
-
pp_enteriter (
PL_op->op_flags & OPf_STACKED
检查“S”)
-
pp_iter.