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.