- 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 iteration |
# 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