This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Tcl performance, script optimization

Issues often discussed about Tcl scripting. Check before posting a scripting question.
Post Reply
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Tcl performance, script optimization

Post by demond »

a fast in-place list update (courtesy of DKF):

Code: Select all

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: Select all

[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
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Post by demond »

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: Select all

if {[llength $alist] == 0}
and not:

Code: Select all

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: Select all

foreach {foo bar moo} [split $args] {break}
is faster and more compact than:

Code: Select all

set foo [lindex [split $args] 0]
set bar [lindex [split $args] 1]
set moo [lindex [split $args] 2]
User avatar
sKy
Op
Posts: 194
Joined: Thu Apr 14, 2005 5:58 pm
Location: Germany

Post by sKy »

regular

Code: Select all

set result [expr 1000 + 1000]

Code: Select all

set result [expr {1000 + 1000}]
set result [expr { 1000 + 1000 * 1000 } ]
This will be a little big faster.
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Post by demond »

not only [expr] arguments, but all expressions in Tcl should be enclosed in braces; I've the bad habit to write:

Code: Select all

if [command $x $y $z] {
   # stuff
}
don't do that; use:

Code: Select all

if {[command $x $y $z]} {
   # stuff
}
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Post by demond »

yet more performance hints:

put everything in a proc, inline Tcl code doesn't get that much optimized like byte-compiled proc does:

Code: Select all

% 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: Select all

regexp "\[ \t\n\r\]" $str
instead, save it into variable first and then use that variable:

Code: Select all

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: Select all

catch {set b $a}

Code: Select all

if {[info exists a]} {set b $a}
User avatar
sKy
Op
Posts: 194
Joined: Thu Apr 14, 2005 5:58 pm
Location: Germany

Post by sKy »

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: Select all

proc te10 { } {
	putlog "te10: [hand2nick $::owner]"
	putlog "te10: [hand2nick $::owner]"
	putlog "te10: [hand2nick $::owner]"
}
- faster:

Code: Select all

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.
M
MrStonedOne
Voice
Posts: 8
Joined: Sun Feb 25, 2007 8:36 am

if and set

Post by MrStonedOne »

im not sure how faster this is... but remember, the set command returns what was just set on something. so

Code: Select all

if {[set nick [hand2nick $::owner]] == "blah"} 
putlog "$nick"
is faster then

Code: Select all

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)
Post Reply