Procedure calling timed

MS: please visit also Speed of different method dispatching methods


The simplest way to call a procedure, namespaced or not, is (1) to give its full name, with arguments. For a proc exported in a namespace, you might also (2) import it so you call it by 'basename', omitting the namespace path. Also, you can (3) instruct any interpreter that a proc name like foo is an alias for another like bar, which may also have constant arguments (minor commands) appended:

 interp alias {} foo {} bar grill

Finally, you can (4) indirectly wrap a proc invocation in another proc:

 proc foo {x y} {bar $x $y}

Miguel Sofer has timed these four alternatives, and to our surprise found out that aliassing (3) takes double the time of (1) or (2), so even (4) is clearly better: ...on 8.4a2, at least! On my notebook I get

 direct       : 299050 microseconds per iteration
 import       : 288499 microseconds per iteration
 interp alias : 638389 microseconds per iteration
 indirect     : 365086 microseconds per iteration

when I run "t 10000" (a few times, until timings stabilize ...) after defining the script below.


Michael Schlenker has run the test with 8.4.1 (w2k, PII 350) and got:

 direct       : 361965 microseconds per iteration
 import       : 399422 microseconds per iteration
 interp alias : 497514 microseconds per iteration
 indirect     : 482034 microseconds per iteration

Seems like the penalty is getting smaller.

NEM 2008-11-13: Updated for Tcl 8.6a4 (MacOS X, dual-core 2GHz):

 direct       : 17720 microseconds per iteration
 import       : 15187 microseconds per iteration
 interp alias : 19742 microseconds per iteration
 indirect     : 21327 microseconds per iteration

Seems like the overhead has vanished.


 namespace eval mig {
    proc test {} {set ::a 5}
    namespace export *
 }
 namespace import mig::*
 interp alias {} test2 {} ::mig::test
 proc test3 {} {::mig::test}

 proc t {n} {
    set t1 [time {
 for {set nn $n} {[incr nn -1]} {} {
     ::mig::test
 }
    }]
    set t2 [time {
 for {set nn $n} {[incr nn -1]} {} {
     test
 }
    }]
    set t3 [time {
 for {set nn $n} {[incr nn -1]} {} {
     test2
 }
    }]
    set t4 [time {
 for {set nn $n} {[incr nn -1]} {} {
     test3
 }
    }]

    puts "\n"
    puts "direct       : $t1"
    puts "import       : $t2"
    puts "interp alias : $t3"
    puts "indirect     : $t4"
 }

RS - I would not have expected such a penalty for an interp alias. What do you others think?

DKF: The "surprising" thing is how different aliases are from namespace imports.

JBR: Or even why an import is faster than directly naming the proc??