In the following example, we compare a Tcl implementation for multiple stacks using a pure Tcl namespace, with two implemented designs using Toe. One Toe design, with inheritance, includes interfaces to support multiple stacks, queues and deques equally. The other Toe design, without inheritance, is functionally equivalent to the pure Tcl approach.

Performance differences were observed, and suggest that performance using Toe classes is comparable to pure Tcl code, but, as expected, creating and deleting Toe objects can have a performance cost.

Reference code in Tcl for multiple stacks, using a namespace

namespace eval ::stack {
  variable arr
  array set arr {}
  proc push {s item} {
    variable arr
    set arr($s) [linsert $arr($s) 0 "$item"]
    return
  }
  proc pop {s} {
    variable arr
    set arr($s) [lassign $arr($s) item]
    return $item
  }
  proc peek {s}  {variable arr ; return [lindex $arr($s) 0]}
  proc clear {s} {variable arr ; set arr($s) {} ; return}
  proc size {s}  {variable arr ; return [llength $arr($s)]}
}

OO code using Toe for multiple stacks, queues and deques,
with inheritance and an abstracted interface

toe interface IDeque {
  method push item
  method poke item
  method pop
  method pull
}

toe interface IDequeAdmin {
  method clear
  method size
}

toe interface IStack {
  method push item
  method pop {}
}

toe interface IQueue {
  method push item
  method pull {}
}

toe class TAbstractDeque abstracts IDeque implements IDequeAdmin {
  protected variable List {}
  public:
  method clear {} {set List {}}
  method size {} {return [llength $List]}
}

toe class TDeque inherits TAbstractDeque implements IDeque {
  public:
  method push {item} {set List [linsert $List 0 "$item"];return}
  method poke {item} {lappend List "$item";return}
  method pull {} {
    K [lindex $List end] [set List [lrange $List 0 end-1]]
  }
  method pop {} {set List [lassign $List item];return $item}
  private method K {x y} {set x}
}

toe class TStack inherits TAbstractDeque implements IStack {
  public:
  method push {item} {set List [linsert $List 0 "$item"];return}
  method pop {} {set List [lassign $List item];return $item}
  method peek {} {return [lindex $List 0]}
}

toe class TQueue inherits TAbstractDeque implements IQueue {
  public:
  method push {item} {set List [linsert $List 0 "$item"];return}
  method pull {} {
    K [lindex $List end] [set List [lrange $List 0 end-1]]
  }
  method peek {} { return [lindex $List end] }
  private method K {x y} {set x}
}

OO code using Toe for multiple stacks,
without inheritance or an abstracted interface

toe interface IStack {
  method push item
  method pop {}
  method size {}
  method peek {}
  method clear {}
}

toe class TStack implements IStack {
  private variable List {}
  public:
  method push {item} {set List [linsert $List 0 "$item"];return}
  method pop {} {set List [lassign $List item];return $item}
  method size {} {return [llength $List]}
  method peek {} {return [lindex $List 0]}
  method clear {} {set List {};return}
}

Reference test cases in Tcl

Tcl test case #1 -- no namespace construction in unit test
set res {}
set tclTime1 [time {
  ::stack::clear s
  ::stack::push s A
  ::stack::push s B
  ::stack::push s C
  lappend res [list \
  [::stack::size s ] [::stack::peek s]\
  [::stack::pop s] [::stack::pop s] [::stack::pop s]\
  ]
} 1000 ]

#--------------------------
Tcl test case #2 -- namespace construction included in unit test
set tclTime2 [time {
  namespace eval ::stack {
  variable arr
  array set arr {}
  proc push {s item} {
    variable arr
    set arr($s) [linsert $arr($s) 0 "$item"]
    return
  }
  proc pop {s} {
    variable arr
    set arr($s) [lassign $arr($s) item]
    return $item
  }
  proc peek {s} {variable arr;return [lindex $arr($s) 0]}
  proc clear {s} {variable arr;set arr($s) {}}
  proc size {s} {variable arr;return [llength $arr($s)]}
  }

  ::stack::clear s
  ::stack::push s A
  ::stack::push s B
  ::stack::push s C
  lappend res [list \
  [::stack::size s ] [::stack::peek s]\
  [::stack::pop s] [::stack::pop s] [::stack::pop s]\
  ]
} 1000 ]

Reference test cases in Toe

Toe test case #1 -- neither object construction nor object reset
set s [new TStack]

set toeTime1 [time {
  $s clear
  $s push a
  $s push b
  $s push c
  lappend res2 [list [$s size] [$s peek] [$s pop] [$s pop] [$s pop]]
} 1000 ]
#--------------------------
Toe test case #2 -- object reset without delete/new
set toeTime2 [time {
  toe reset $s
  $s push a
  $s push b
  $s push c
  lappend res2 [list [$s size] [$s peek] [$s pop] [$s pop] [$s pop]]
} 1000 ]
#--------------------------
Toe test case #3 -- object new/delete per test cycle
set toeTime3 [time {
  set s [toe new TStack]
  $s push a
  $s push b
  $s push c
  lappend res2 [list [$s size] [$s peek] [$s pop] [$s pop] [$s pop]]
  toe delete $s
} 1000 ]

Expected performance
ranking, fastest first


Tcl #1 (no construction)
Toe #1 (no construction)
Tcl #2 or Toe #2
Toe #3 (with construction)

Performance with inheritance

Test caseTime, microseconds
Tcl #111
Toe #112
Tcl #263
Toe #283
Toe #3868

Performance without inheritance

Test caseTime, microseconds
Tcl #111
Toe #112
Tcl #263
Toe #257
Toe #3379

Conclusions

  • Toe's code execution is comparable to Tcl's, for a simple class
  • Resetting a Toe object, or object hierarchy, is comparable to resetting namespace variables, with or without inheritance
  • In terms of performance, "new" and "delete" are expensive; a cycle of delete and new to "refresh" an instance of the same class is several-fold slower than resetting the Toe object, and even slower with inheritance.