- there is a class "dad", residing in namespace "::dad"
- the class has a (here trivial) method "proc test {x args} {set x}"
- an object "son" of class "dad" resides in namespace "::son"
- the user wants to call son's test
- Plain proc call Just copy the proc ::dad::test to ::son::test . Updating the class proc is a real pain with this method, adding class procs and changing class membership too.
- namespace import Import ::dad::test into ::son . . Updating is very easy, changing class membership is easy, adding class procs is a pain. (cobj2)
- interp alias Alias ::son::test to ::dad::test . Similar properties to 2. above.
- indirect through proc Define ::son::test to call ::dad::test . Similar properties to 2. above.
- store in array Keep a global array to redirect the calls (I think stooop [1] uses this approach). Similar properties to 2. above.
- switch Store the redirects in the body of an object dispatcher, which switches according to name. (gadgets, LOST, cobj). Updating class procs is easy, changing class membership less so, adding class procs is a pain.
- lookup Son knows his heritage; it's dispatcher looks in the ancestors until it finds a suitable proc (Chaining things). Updating class procs, adding class procs and changing class membership is VERY easy.
- inline For comparison, to estimate how much time was spent actually performing the proc
- noop To see how much loop overhead there is in the measurement
% source sent_corr direct : 16 microseconds per iteration import : 16 microseconds per iteration interp alias : 51 microseconds per iteration indirect : 67 microseconds per iteration array : 27 microseconds per iteration switch : 99 microseconds per iteration lookup : 223 microseconds per iteration inline : 7 microseconds per iteration noop : 3 microseconds per iteration
HK: Tried this benchmark on a Sun Blade 1500 running on tcl8.4.6. Here are the timings:
direct : 2 microseconds per iteration import : 3 microseconds per iteration interp alias : 3 microseconds per iteration indirect : 10 microseconds per iteration array : 3 microseconds per iteration switch : 13 microseconds per iteration lookup : 24 microseconds per iteration inline : 1 microseconds per iteration noop : 0 microseconds per iterationApparently the interp alias method has been improved a bit, now being a viable alternative.
# The code
namespace eval dad {
proc test {x args} {set x}
test 5
namespace export *
}
namespace eval son {}
proc t {n} {
# 1. Plain proc call
proc ::son::test {x args} {set x}
::son::test 5
set t [time {::son::test 5} $n]
namespace eval :: {rename ::son::test {}}
puts " direct : $t"
# 2. namespace import
namespace eval ::son namespace import -force ::dad::*
set t [time {::son::test 5} $n]
puts " import : $t"
# 3. interp alias
interp alias {} ::son::test {} ::dad::test
set t [time {::son::test 5} $n]
interp alias {} ::son::test {}
puts " interp alias : $t"
# 4. indirect through proc
proc ::son::test {x args} {eval [linsert $args 0 ::dad::test $x]}
::son::test 5
set t [time {::son::test 5} $n]
namespace eval :: {rename ::son::test {}}
puts " indirect : $t"
# 5. store in array (it's name is the empty string!)
set ::(::son::test) ::dad::test
set t [time {$::(::son::test) 5} $n]
puts " array : $t"
# 6. switch
proc ::son {method args} {
switch $method {
a {}
b {}
test {return [eval [linsert $args 0 ::dad::test]]}
}
}
::son test 5
set t [time {::son test 5} $n]
namespace eval :: {rename ::son {}}
puts " switch : $t"
# 7. lookup
set ::b [list ::dad ::none]
proc ::son {method args} {
foreach anc $::b {
if {[llength [info proc ${anc}::$method]]} {
return [eval [linsert $args 0 ${anc}::$method]]
}
}
}
::son test 5
set t [time {::son test 5} $n]
namespace eval :: {rename ::son {}}
puts " lookup : $t"
# 8. inline
set x 5
set t [time {set x} $n]
puts " inline : $t"
# 9. empty loop
set x 5
set t [time {} $n]
puts " noop : $t"
}
t 30000