TCL 全局 vs. 可变访问速度

TCL global vs. variable access speed

在TCL中,大致有全局变量,可通过global varname访问,或命名空间变量可通过namespace varname命令访问。
我非常简单的访问时间检查对于 2,对于版本 tcl 8.5.
variableglobal 相比显示了微小但一致的运行时访问优势 我们是否可以期望 8.6 及更高版本会出现这种情况,至少目前如此?

tl;dr: 两者在速度上非常 差别不大,您应该不在意。以语义上正确的方式做事并保持原样。对于您的用例 正确 将击败任何在时间方面进行百分之几的修补。


让我们比较一下全局命名空间中的以下两个过程做同样的事情。请注意,我们正在比较的选项之间的差异权重将相对于实际代码被人为提高;这些是微基准测试,理应受到怀疑。

proc a {} {
    global x y
    expr {$x + $y}
}

proc b {} {
    variable x
    variable y
    expr {$x + $y}
}

好的,计时信息(您的绝对计时 因硬件而异):

% set x 123
123
% set y 456
456
% a
579
% b
579
% time { a } 10000
0.5720533 microseconds per iteration
% time { b } 10000
0.5756787999999999 microseconds per iteration

所以是的,ba 稍慢。让我们看看字节码看看为什么:

% tcl::unsupported::disassemble proc a
ByteCode 0x0x10280fe10, refCt 1, epoch 15, interp 0x0x100829a10 (epoch 15)
  Source "\n    global x y\n    expr {$x + $y"...
  Cmds 2, src 35, inst 35, litObjs 4, aux 0, stkDepth 2, code/src 0.00
  Proc 0x0x102822c10, refCt 1, args 0, compiled locals 2
      slot 0, scalar, "x"
      slot 1, scalar, "y"
  Commands 2:
      1: pc 0-19, src 5-14        2: pc 20-33, src 20-33
  Command 1: "global x y"...
    (0) push1 0     # "::"
    (2) push1 1     # "x"
    (4) nsupvar %v0     # var "x"
    (9) push1 2     # "y"
    (11) nsupvar %v1    # var "y"
    (16) pop 
    (17) nop 
    (18) nop 
    (19) nop 
  Command 2: "expr {$x + $y}"...
    (20) startCommand +14 1     # next cmd at pc 34, 1 cmds start here
    (29) loadScalar1 %v0    # var "x"
    (31) loadScalar1 %v1    # var "y"
    (33) add 
    (34) done 

% tcl::unsupported::disassemble proc b
ByteCode 0x0x10280fa10, refCt 1, epoch 15, interp 0x0x100829a10 (epoch 15)
  Source "\n    variable x\n    variable y\n    expr {$x + $"...
  Cmds 3, src 50, inst 44, litObjs 3, aux 0, stkDepth 2, code/src 0.00
  Proc 0x0x102822a90, refCt 1, args 0, compiled locals 2
      slot 0, scalar, "x"
      slot 1, scalar, "y"
  Commands 3:
      1: pc 0-9, src 5-14        2: pc 10-28, src 20-29
      3: pc 29-42, src 35-48
  Command 1: "variable x"...
    (0) push1 0     # "x"
    (2) variable %v0    # var "x"
    (7) nop 
    (8) nop 
    (9) nop 
  Command 2: "variable y"...
    (10) startCommand +18 1     # next cmd at pc 28, 1 cmds start here
    (19) push1 2    # "y"
    (21) variable %v1   # var "y"
    (26) push1 1    # ""
    (28) pop 
  Command 3: "expr {$x + $y}"...
    (29) startCommand +14 1     # next cmd at pc 43, 1 cmds start here
    (38) loadScalar1 %v0    # var "x"
    (40) loadScalar1 %v1    # var "y"
    (42) add 
    (43) done 

确切的字节码序列可能与您的 Tcl 版本不同;这完全是实施的一部分,而不是官方界面。但是,我们可以看到,对于 global,操作码序列涉及命名空间句柄的单个 push1 和命令实现中的两个 nsupvar,以及两个 variable 调用时,名称空间未被推送,但 variable 操作码被使用了两次,并且两者之间有一个额外的 startCommandstartCommand 是一个混乱的基础设施,它有抑制一些小优化的副作用,所以这可能是原因。

由于两者之间的差异如此之小,找出性能差异的真正所在将需要像 cachegrind 这样的仪器……

顺便说一句,尝试将性能与此进行比较:

proc c {} {
    namespace upvar :: x x y y
    expr {$x + $y}
}

它有一个 与上面的 a 过程完全相同的 字节码序列…