Posted to tcl by apn at Thu May 05 02:44:34 GMT 2022view pretty

I was surprised by the following

(win) 1 % proc x {l e} {linsert $l 0 $e}
(win) 2 % proc y {l args} {linsert $l 0 {*}$args}
(win) 3 % set l {} ; time {set l [x $l a]} 50000
95.58538800000001 microseconds per iteration
(win) 4 % set l {} ; time {set l [y $l a]} 50000
82.88127 microseconds per iteration

Proc y is more than 10% faster despite the use of args and {*}

Disassembling shows the difference

(win) 5 % tcl::unsupported::disassemble proc x
ByteCode 0x0000028C3E210D50, refCt 1, epoch 17, interp 0x0000028C3B79C620 (epoch 17)
  Source "linsert $l 0 $e"
  Cmds 1, src 15, inst 16, litObjs 0, aux 0, stkDepth 2, code/src 0.00
  Proc 0x0000028C3E1D2D90, refCt 1, args 2, compiled locals 2
      slot 0, scalar, arg, "l"
      slot 1, scalar, arg, "e"
  Commands 1:
      1: pc 0-14, src 0-14
  Command 1: "linsert $l 0 $e"
    (0) loadScalar1 %v0 	# var "l"
    (2) loadScalar1 %v1 	# var "e"
    (4) list 1 
    (9) reverse 2 
    (14) listConcat 
    (15) done 

(win) 6 % tcl::unsupported::disassemble proc y
ByteCode 0x0000028C3E211A50, refCt 1, epoch 17, interp 0x0000028C3B79C620 (epoch 17)
  Source "linsert $l 0 {*}$args"
  Cmds 1, src 21, inst 16, litObjs 2, aux 0, stkDepth 4, code/src 0.00
  Proc 0x0000028C3E1C4E50, refCt 1, args 2, compiled locals 2
      slot 0, scalar, arg, "l"
      slot 1, scalar, arg, "args"
  Commands 1:
      1: pc 0-14, src 0-20
  Command 1: "linsert $l 0 {*}$args"
    (0) expandStart 
    (1) push1 0 	# "linsert"
    (3) loadScalar1 %v0 	# var "l"
    (5) push1 1 	# "0"
    (7) loadScalar1 %v1 	# var "args"
    (9) expandStkTop 4 
    (14) invokeExpanded 
    (15) done 

If I understand that correctly, y invokes the C implemented Tcl_LinsertObjCmd while x does it all in byte code which is measurably slower.