This page is about benchmarking Tcl's proc vs Itcl-based code to see how much overhead Itcl adds and how to make its performance better.
I decided to compare plain Tcl recursive calling vs Itcl inheriting, using itcl chain and delegating.
If Itcl class has no variables and procs do not use any variables:
ITcl: Chain 10 level deep 51.347 ITcl: Chain 11 level deep 55.769 ITcl: Chain 12 level deep 61.921 ITcl: Chain 13 level deep 72.064 ITcl: Chain 14 level deep 76.513 ITcl: Chain 15 level deep 77.732 ITcl: Chain 16 level deep 84.874 ITcl: Chain 17 level deep 91.932 ITcl: Chain 18 level deep 121.868 ITcl: Chain 19 level deep 114.08 ITcl: Chain 20 level deep 108.025 ITcl: Delegate 10 level deep 32.479 ITcl: Delegate 11 level deep 36.436 ITcl: Delegate 12 level deep 39.776 ITcl: Delegate 13 level deep 41.741 ITcl: Delegate 14 level deep 45.113 ITcl: Delegate 15 level deep 49.032 ITcl: Delegate 16 level deep 52.265 ITcl: Delegate 17 level deep 54.443 ITcl: Delegate 18 level deep 93.945 ITcl: Delegate 19 level deep 71.611 ITcl: Delegate 20 level deep 70.01 ITcl: Inherit 10 level deep 25.959 ITcl: Inherit 11 level deep 28.699 ITcl: Inherit 12 level deep 31.088 ITcl: Inherit 13 level deep 33.196 ITcl: Inherit 14 level deep 35.765 ITcl: Inherit 15 level deep 38.178 ITcl: Inherit 16 level deep 44.797 ITcl: Inherit 17 level deep 43.085 ITcl: Inherit 18 level deep 54.318 ITcl: Inherit 19 level deep 47.477 ITcl: Inherit 20 level deep 50.636 Tcl: Recursive 10 level deep 10.403 Tcl: Recursive 11 level deep 11.743 Tcl: Recursive 12 level deep 12.324 Tcl: Recursive 13 level deep 14.245 Tcl: Recursive 14 level deep 14.721 Tcl: Recursive 15 level deep 16.241 Tcl: Recursive 16 level deep 16.997 Tcl: Recursive 17 level deep 17.485 Tcl: Recursive 18 level deep 18.763 Tcl: Recursive 19 level deep 22.154 Tcl: Recursive 20 level deep 21.667
So it seems that inheriting and calling methods from the same object seem to be the fastest way and using chain is the worst way.
But things change when we define 20 variables at each level - so a 20-level-deep test actually increments 19 * 20 = 380 variables:
ITcl: Chain 10 level deep 104.602 ITcl: Chain 11 level deep 112.974 ITcl: Chain 12 level deep 126.078 ITcl: Chain 13 level deep 138.874 ITcl: Chain 14 level deep 147.253 ITcl: Chain 15 level deep 161.369 ITcl: Chain 16 level deep 173.584 ITcl: Chain 17 level deep 188.603 ITcl: Chain 18 level deep 198.702 ITcl: Chain 19 level deep 242.112 ITcl: Chain 20 level deep 226.422 ITcl: Delegate 10 level deep 59.411 ITcl: Delegate 11 level deep 68.117 ITcl: Delegate 12 level deep 70.796 ITcl: Delegate 13 level deep 78.662 ITcl: Delegate 14 level deep 84.386 ITcl: Delegate 15 level deep 90.361 ITcl: Delegate 16 level deep 96.495 ITcl: Delegate 17 level deep 103.415 ITcl: Delegate 18 level deep 109.857 ITcl: Delegate 19 level deep 116.133 ITcl: Delegate 20 level deep 121.538 ITcl: Inherit 10 level deep 75.653 ITcl: Inherit 11 level deep 105.711 ITcl: Inherit 12 level deep 92.505 ITcl: Inherit 13 level deep 104.654 ITcl: Inherit 14 level deep 107.463 ITcl: Inherit 15 level deep 120.157 ITcl: Inherit 16 level deep 150.662 ITcl: Inherit 17 level deep 134.11 ITcl: Inherit 18 level deep 144.124 ITcl: Inherit 19 level deep 154.595 ITcl: Inherit 20 level deep 163.931 Tcl: Recursive 10 level deep 61.822 Tcl: Recursive 11 level deep 68.536 Tcl: Recursive 12 level deep 79.564 Tcl: Recursive 13 level deep 82.756 Tcl: Recursive 14 level deep 93.726 Tcl: Recursive 15 level deep 96.324 Tcl: Recursive 16 level deep 102.629 Tcl: Recursive 17 level deep 109.943 Tcl: Recursive 18 level deep 118.434 Tcl: Recursive 19 level deep 123.855 Tcl: Recursive 20 level deep 129.817
The most interesting part is that delegates now seem to work much faster than the other ones. Still, 25% performance loss using inheritance and chain having the biggest loss. The interesting part is that delegates now caught up with procs.
Now, for 50 variables (so 1000 variable references with 20th level):
ITcl: Chain 10 level deep 172.473 ITcl: Chain 11 level deep 194.999 ITcl: Chain 12 level deep 244.775 ITcl: Chain 13 level deep 242.553 ITcl: Chain 14 level deep 273.848 ITcl: Chain 15 level deep 304.169 ITcl: Chain 16 level deep 337.07 ITcl: Chain 17 level deep 370.41 ITcl: Chain 18 level deep 432.148 ITcl: Chain 19 level deep 466.136 ITcl: Chain 20 level deep 510.95 ITcl: Delegate 10 level deep 98.282 ITcl: Delegate 11 level deep 113.818 ITcl: Delegate 12 level deep 118.42 ITcl: Delegate 13 level deep 128.619 ITcl: Delegate 14 level deep 140.015 ITcl: Delegate 15 level deep 153.629 ITcl: Delegate 16 level deep 171.915 ITcl: Delegate 17 level deep 175.932 ITcl: Delegate 18 level deep 193.936 ITcl: Delegate 19 level deep 212.568 ITcl: Delegate 20 level deep 259.58 ITcl: Inherit 10 level deep 146.439 ITcl: Inherit 11 level deep 164.215 ITcl: Inherit 12 level deep 183.119 ITcl: Inherit 13 level deep 199.634 ITcl: Inherit 14 level deep 232.69 ITcl: Inherit 15 level deep 269.736 ITcl: Inherit 16 level deep 270.989 ITcl: Inherit 17 level deep 336.297 ITcl: Inherit 18 level deep 338.85 ITcl: Inherit 19 level deep 369.274 ITcl: Inherit 20 level deep 423.055 Tcl: Recursive 10 level deep 130.904 Tcl: Recursive 11 level deep 156.326 Tcl: Recursive 12 level deep 160.326 Tcl: Recursive 13 level deep 179.524 Tcl: Recursive 14 level deep 189.009 Tcl: Recursive 15 level deep 208.528 Tcl: Recursive 16 level deep 219.666 Tcl: Recursive 17 level deep 232.773 Tcl: Recursive 18 level deep 253.428 Tcl: Recursive 19 level deep 287.717 Tcl: Recursive 20 level deep 304.345
This is very interesting because the test claims that delegates using Itcl can actually be faster than non-OO programming - which usually should not be the case.
The tclbench-based test that did the trick:
package require Itcl set itclvars 0 set minlevel 10 set maxlevel 20 set iter 10000 proc ::level1 {a b c d e} { } itcl::class ::class1 { public method level1 {a b c d e} { return 1 } } itcl::class ::classI1 { inherit ::class1 } itcl::class ::classD1 { inherit ::class1 } itcl::class ::classC1 { public method level {a b c d e} { return 1 } } for {set i 2} {$i <= $maxlevel} {incr i} { set varcode "" set varproccode "" set varitclcode "" set varlist [list] for {set j 0} {$j < $itclvars} {incr j} { set vn v${i}c${j} lappend varlist $vn append varcode "protected variable $vn 0" \n set ::$vn 0 append varproccode "incr ::$vn" \n append varitclcode "incr $vn" \n } set ip [expr {$i-1}] proc ::level$i {a b c d e} "$varproccode \; return \[::level$ip \$a \$b \$c \$d \$e\]" itcl::class ::classD$i " $varcode public method level$i \{a b c d e\} \{ $varitclcode return \[::oD$ip level$ip \$a \$b \$c \$d \$e\] \} " itcl::class ::classI$i "inherit ::classI$ip $varcode public method level$i \{a b c d e\} \{ $varitclcode return \[level$ip \$a \$b \$c \$d \$e\] \} " itcl::class ::classC$i "inherit ::classC$ip $varcode public method level \{a b c d e\} \{ $varitclcode return \[chain \$a \$b \$c \$d \$e\] \} " } for {set i 1} {$i <= $maxlevel} {incr i} { ::classI$i ::oI$i ::classD$i ::oD$i ::classC$i ::oC$i } if {[catch { ::oI$maxlevel level$maxlevel 1 2 3 4 5 ::oD$maxlevel level$maxlevel 1 2 3 4 5 ::oC2 level 1 2 3 4 5 ::level2 1 2 3 4 5 }]} { puts stderr $::errorInfo exit 1 } for {set i $minlevel} {$i <= $maxlevel} {incr i} { bench -desc "Tcl: Recursive [format %2d $i] level deep" -iter $iter \ -body "::level$i a b c d e" bench -desc "ITcl: Inherit [format %2d $i] level deep" -iter $iter \ -body "::oI$i level$i a b c d e" bench -desc "ITcl: Delegate [format %2d $i] level deep" -iter $iter \ -body "::oD$i level$i a b c d e" bench -desc "ITcl: Chain [format %2d $i] level deep" -iter $iter \ -body "::oC$i level a b c d e" }
If someone wishes to make improvements to the code, please also update the benchmarks.