View previous topic :: View next topic |
Author |
Message |
demond Revered One

Joined: 12 Jun 2004 Posts: 3073 Location: San Francisco, CA
|
Posted: Fri Jul 29, 2005 11:16 pm Post subject: Tcl performance, script optimization |
|
|
a fast in-place list update (courtesy of DKF):
Code: |
proc K {x y} {set x}
set theList [lreplace [K $theList [set theList {}]] 7 42]
|
what happens here? when you use [lreplace] the usual way, entire list gets duplicated (not elements though), manipulated and then copied back to the list variable, then the duplicate is destroyed
when you use the above method with so-called K combinator (an important notion in functional programming), list manipulation is done in-place, meaning duplication is eliminated (the K combinator in this case is functioning as get-and-unset proc)
on my machine, it's almost 50 times faster! operating on a big list of course:
Code: |
[demond@whitepine demond]$ tclsh8.4
% proc K {a b} {set a}
% for {set i 0} {$i<100000} {incr i} {
lappend a $i; lappend b $i
}
% time {set a [lreplace $a 40 80]}
24879 microseconds per iteration
% time {set b [lreplace [K $b [set b {}]] 40 80]}
537 microseconds per iteration
|
|
|
Back to top |
|
 |
demond Revered One

Joined: 12 Jun 2004 Posts: 3073 Location: San Francisco, CA
|
Posted: Wed Aug 10, 2005 4:09 am Post subject: |
|
|
more performance tips:
Data types in Tcl 8.0 and greater are represented in Tcl_Obj internal object type; don't change the type of that representation if you can, for example use:
Code: |
if {[llength $alist] == 0}
|
and not:
Code: |
if {[string compare {} $alist] == 0}
|
(in the latter expression the internal representation of the argument list $alist is changed to string, which hurts the performance)
this might not be much relevant to eggdrop's bind triggers (which normally wouldn't be a performance bottleneck), but still:
Code: |
foreach {foo bar moo} [split $args] {break}
|
is faster and more compact than:
Code: |
set foo [lindex [split $args] 0]
set bar [lindex [split $args] 1]
set moo [lindex [split $args] 2]
|
|
|
Back to top |
|
 |
sKy Op

Joined: 14 Apr 2005 Posts: 194 Location: Germany
|
Posted: Wed Aug 10, 2005 12:51 pm Post subject: |
|
|
regular
Code: | set result [expr 1000 + 1000] |
Code: | set result [expr {1000 + 1000}]
set result [expr { 1000 + 1000 * 1000 } ] |
This will be a little big faster. |
|
Back to top |
|
 |
demond Revered One

Joined: 12 Jun 2004 Posts: 3073 Location: San Francisco, CA
|
Posted: Fri Aug 12, 2005 2:38 pm Post subject: |
|
|
not only [expr] arguments, but all expressions in Tcl should be enclosed in braces; I've the bad habit to write:
Code: |
if [command $x $y $z] {
# stuff
}
|
don't do that; use:
Code: |
if {[command $x $y $z]} {
# stuff
}
|
|
|
Back to top |
|
 |
demond Revered One

Joined: 12 Jun 2004 Posts: 3073 Location: San Francisco, CA
|
Posted: Fri Aug 12, 2005 3:32 pm Post subject: |
|
|
yet more performance hints:
put everything in a proc, inline Tcl code doesn't get that much optimized like byte-compiled proc does:
Code: |
% time {for {set i 0} {$i<10000} {incr i} {lappend a $i}} 100
35300 microseconds per iteration
% proc init_me {} {
global b
for {set i 0} {$i<10000} {incr i} {lappend b $i}
}
% time {init_me} 100
21888 microseconds per iteration
|
don't use inline regexps too much:
Code: |
regexp "\[ \t\n\r\]" $str
|
instead, save it into variable first and then use that variable:
Code: |
set ws "\[ \t\n\r\]"
...
regexp $ws $str
|
the second form allows storing the compiled regexp in the Tcl_Obj representation of $ws and simply executing it on demand, whereas the first form could lead to compiling that regexp again and again (after interpreter's regexp cache has been exhausted)
don't use [catch] too often, it's slow and should be used only when you are pretty certain that it triggers very rarely; for example, this lazy line of code is 10 times slower than the [info exists] construct that follows it (provided $a doesn't exist):
Code: |
if {[info exists a]} {set b $a}
|
|
|
Back to top |
|
 |
sKy Op

Joined: 14 Apr 2005 Posts: 194 Location: Germany
|
Posted: Sat Aug 13, 2005 8:45 pm Post subject: |
|
|
If you want to use the return code from a command/proc serval times i suggest you to use the set command and not to execute it each time.
Examples (just to show you what i mean):
- slow:
Code: | proc te10 { } {
putlog "te10: [hand2nick $::owner]"
putlog "te10: [hand2nick $::owner]"
putlog "te10: [hand2nick $::owner]"
} | - faster:
Code: | proc te10 { } {
set ownerhand [hand2nick $::owner]
putlog "te10: $ownerhand"
putlog "te10: $ownerhand"
putlog "te10: $ownerhand"
} |
Very second i might add, in my personal opinion a script looks more easy and clear if you use the set command more often then executing all this stuff over and over again. |
|
Back to top |
|
 |
MrStonedOne Voice
Joined: 25 Feb 2007 Posts: 8
|
Posted: Tue Feb 10, 2009 7:45 am Post subject: if and set |
|
|
im not sure how faster this is... but remember, the set command returns what was just set on something. so Code: | if {[set nick [hand2nick $::owner]] == "blah"}
putlog "$nick" |
is faster then
Code: |
if {[hand2nick $::owner] == "blah"}
putlog "[hand2nick $::owner]"
|
and as you see here... using ::varName is faster for accessing globals then to map it with the global command (unless your gonna be accessing a global alot in one proc. then the command is faster) |
|
Back to top |
|
 |
|