gpt4 book ai didi

TCL proc 和字节码编译 - 链接是什么?

转载 作者:行者123 更新时间:2023-12-01 22:23:29 25 4
gpt4 key购买 nike

我好几次提到最好将脚本放入 proc 以提高运行时性能,例如this answer具有以下内容:

That is one reason for the advices to put all your code inside procedures (they get byte-compiled that way)

有些东西没有点击我。

正如答案中所述,第一次运行脚本时,会检查命令是否可以字节码编译,如果可以,则进行编译。这是完全有道理的。但我没有看到“proc”如何发挥重要作用。例如。比较以下 2 个脚本:

set v [concat [lindex $::argv 1] [lindex $::argv 2]]
myCmd $v

proc p1 {v1 v2} {
set v [concat $v1 $v2]
return [myCmd $v]
}
p1 [lindex $::argv 1] [lindex $::argv 2]

我对这 2 个脚本的高级解释如下:

  1. In running either script the first time, "set", "concat", "lindex" and "return" commands are compiled
  2. The second script also has "proc" compiled.
  3. "myCmd" is not compiled in either script
  4. Subsequent running of either script runs the bycode except "myCmd".

那么“proc”有什么优势呢?

我确实在脚本上运行了 dissamble:

第一个脚本:

ByteCode 0x0x83fc70, refCt 1, epoch 3, interp 0x0x81d680 (epoch 3)
Source "set v [concat [lindex $::argv 1] [lindex $::argv 2]]\nmy"
Cmds 5, src 61, inst 50, litObjs 4, aux 0, stkDepth 4, code/src 0.00
Commands 5:
1: pc 0-41, src 0-51 2: pc 2-39, src 7-50
3: pc 4-20, src 15-30 4: pc 21-37, src 34-49
5: pc 42-48, src 53-60
Command 1: "set v [concat [lindex $::argv 1] [lindex $::argv 2]]"
(0) push1 0 # "v"
Command 2: "concat [lindex $::argv 1] [lindex $::argv 2]"
(2) push1 1 # "concat"
Command 3: "lindex $::argv 1"
(4) startCommand +17 1 # next cmd at pc 21
(13) push1 2 # "::argv"
(15) loadScalarStk
(16) listIndexImm 1
Command 4: "lindex $::argv 2"
(21) startCommand +17 1 # next cmd at pc 38
(30) push1 2 # "::argv"
(32) loadScalarStk
(33) listIndexImm 2
(38) invokeStk1 3
(40) storeScalarStk
(41) pop
Command 5: "myCmd $v"
(42) push1 3 # "myCmd"
(44) push1 0 # "v"
(46) loadScalarStk
(47) invokeStk1 2
(49) done

第二个脚本:

ByteCode 0x0xc06c80, refCt 1, epoch 3, interp 0x0xbe4680 (epoch 3)
Source "proc p1 {v1 v2} {\n set v [concat $v1 $v2]\n return"
Cmds 4, src 109, inst 50, litObjs 5, aux 0, stkDepth 4, code/src 0.00
Commands 4:
1: pc 0-10, src 0-67 2: pc 11-48, src 69-108
3: pc 13-29, src 73-88 4: pc 30-46, src 92-107
Command 1: "proc p1 {v1 v2} {\n set v [concat $v1 $v2]\n return"
(0) push1 0 # "proc"
(2) push1 1 # "p1"
(4) push1 2 # "v1 v2"
(6) push1 3 # "\n set v [concat $v1 $v2]\n return ["
(8) invokeStk1 4
(10) pop
Command 2: "p1 [lindex $::argv 1] [lindex $::argv 2]"
(11) push1 1 # "p1"
Command 3: "lindex $::argv 1"
(13) startCommand +17 1 # next cmd at pc 30
(22) push1 4 # "::argv"
(24) loadScalarStk
(25) listIndexImm 1
Command 4: "lindex $::argv 2"
(30) startCommand +17 1 # next cmd at pc 47
(39) push1 4 # "::argv"
(41) loadScalarStk
(42) listIndexImm 2
(47) invokeStk1 3
(49) done

所以脚本 2 确实少了 1 个 TCL 命令,但是两个脚本都有 49 字节代码命令。

最后是运行测试,我把“myCmd”注释掉了,因为我其实没有这样的扩展。这是结果:

% time {source 1.tcl} 10000
242.8156 microseconds per iteration
% time {source 2.tcl} 10000
257.9389 microseconds per iteration

所以 proc 版本更慢。

我错过了什么?或者更确切地说,proc 和 performance 的确切理解是什么?

最佳答案

把东西放在过程中的真正重要原因是过程有一个局部变量表。 LVT 中的变量可以通过数字索引访问,这比替代方法快得多(通过哈希表查找,即使 Tcl 有一个极快的哈希表实现)。对于一次性调用来说没有太大区别,但是对于重复调用或循环,性能差异会迅速累积起来。这很容易使额外编译和堆栈帧管理的额外成本(过程不是免费输入的,尽管我们尽量保持它们便宜)在实际脚本中基本上无关紧要。

是的,Tcl 实际上字节码编译一切。只是它经常在过程(如上下文)之外生成次优字节码;在次优的极限情况下,所有字节码所做的就是将参数组装到列表中,进行动态命令调用,然后路由结果。

(在阅读 Tcl 的反汇编字节码时记住特定字节码的成本是很重要的。您不能只计算指令的数量以任何有用的方式计算成本。例如,push1 非常便宜,但 invokeStk1 可能非常昂贵。另一个例子,loadScalarStk 通常比 loadScalar1< 昂贵得多;后者仅在程序内部使用。)

关于TCL proc 和字节码编译 - 链接是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38531116/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com