'''[MS]''': While playing with tcl-only OO, an important design decision concerns the method used for dipatching the calls to the different object methods. The speed of the system, and ultimately its usability, is strongly dependent on the speed of the dispatcher. So I decided to time the different possible methods (marked with *) and contrast the timings with plain proc calls. In order to allow dynamic class membership, the complete path to any object method cannot be fixed at compile time. It hence has to be looked up at runtime. The dispatcher's task is to do this lookup for arbitrary method calls, and call the correct method. Let me describe a couple of variants that occurred to me (and kindly request any other suggestions!). The framework is as follows: * 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 We focus here on the speed of dispatching; a different (less important?) concern is the speed at which class membership relationships can be updated. Another concern that we disregard here is the memory requirements for each object. The methods that we compare here are: 1. '''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. 2. '''namespace import''' Import ::dad::test into ::son . . Updating is very easy, changing class membership is easy, adding class procs is a pain. (cobj2 [http://purl.org/thecliff/tcl/wiki/1179.html]) 3. '''interp alias''' Alias ::son::test to ::dad::test . Similar properties to 2. above. 4. '''indirect through proc''' Define ::son::test to call ::dad::test . Similar properties to 2. above. 5. '''store in array''' Keep a global array to redirect the calls (I think stooop [http://jfontain.free.fr/stooop.html] uses this approach). Similar properties to 2. above. 6. '''switch''' Store the redirects in the body of an object dispatcher, which switches according to name. (gadgets [http://purl.org/thecliff/tcl/wiki/909.html], LOST [http://purl.org/thecliff/tcl/wiki/923.html], cobj [http://purl.org/thecliff/tcl/wiki/1179.html]). Updating class procs is easy, changing class membership less so, adding class procs is a pain. 7. '''lookup''' Son knows his heritage; it's dispatcher looks in the ancestors until it finds a suitable proc (things [http://purl.org/thecliff/tcl/wiki/1094.html]). Updating class procs, adding class procs and changing class membership is VERY easy. 8. '''inline''' For comparison, to estimate how much time was spent actually performing the proc 9. '''noop''' To see how much loop overhead there is in the measurement Remark that methods 1-5 call the target proc ONLY; they do not need to eval their args beforehand. Methods 6 and 7 on the other hand have to eval. '''NOTE''': this has been modified to make it fairer and easier to read: (a) the evaling methods now eval a pure list (faster!); (b) we use time instead of a for loop - less loop overhead, purer measurement and result is directly in [[microseconds per call]]; (c) added for comparison the [[lookup]] and [[noop]] results; (d) increased the runs to 30000, which produces much stabler results on my machine. How the methods scale for deeper inheritance may also be interesting; not done here. On my notebook (P166, linux, tcl8.4a2) the timings for the script below are: % 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 Apparently 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