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 case | Time, microseconds |
Tcl #1 | 11 |
Toe #1 | 12 |
Tcl #2 | 63 |
Toe #2 | 83 |
Toe #3 | 868 |
|
Performance without inheritance
Test case | Time, microseconds |
Tcl #1 | 11 |
Toe #1 | 12 |
Tcl #2 | 63 |
Toe #2 | 57 |
Toe #3 | 379 |
|
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.
|
|
|