- Write expr commands with the argument in curly braces whenever possible. This avoids conversion of numbers in strings and back. The speed impact may be a factor of 20 or so.
- If you can't put the whole expression in braces (e.g., because you need to substitute an operator), using an inner expr round the parts that can't be braced will still help. Be very careful though. Unbraced expressions can also be security holes.
- (*)Avoid scripts that are identical to a single command name, as they will be very slow due to a bug [1]. A workaround is to destroy the identity by adding a trailing space or semicolon in the script - ie, using {myProc } or {myProc;} instead of plain {myProc}. Note that this bug is only likely to bite in a [...] substitution, or when the script is an argument to eval, uplevel, namespace eval or similar. It will definitely not be an issue for command invocations within a larger script. In summary, do write eval {cmdWithNoArgs }; set x [cmdWithNoArgs ] (note the trailing spaces)
- Unnecessary to say that procs execute considerably faster due to byte code compiling. But it is worth noting explicitly that bindings do not compile to byte code. Speed critical bindings (e.g. mouse motion bindings) should be delegated to procs. Beware of evaluating performance of code sections with the time command inside procs. It will affect byte code compilation heavily (see note below, also refer to Commands affecting Bytecoding).
- Do not intermix list and string operations where not necessary. For an introduction see shimmering and also pure list.
RJM: Referencing to (2) I can say that I found this by accident. On a Win98 machine with 266 MHz clock, the following execution times apply:An empty proc without Arguments:
% proc test {} {} % time {test} 1000 95 microseconds per iterationAn empty proc with an Argument:
% proc fast {x} {} % time {fast 5} 1000 12 microseconds per iterationI performed several variations, also with filled body and so on. The crucial differencs lies in defining a proc with or without an argument.
MSW: Actually calling a proc without an argument. For comparison:
% time {test} 1000 12 microseconds per iteration % time {fast 5} 1000 3 microseconds per iteration % proc maybe {{empty {}}} {} % time {maybe} 1000 13 microseconds per iteration % time {maybe empty} 1000 3 microseconds per iteration
MS This is Bug #458361 [2], but the interpretation is wrong: it is not proc calls without arguments that are slow, but rather scripts that consist of a single word and whose string equals a command name - which significantly reduces the likelihood of hitting the bug. Even adding a space to the script destroys the effect. Witness
% proc maybe {{empty {}}} {} % # call with argument % time {maybe empty} 10000 4 microseconds per iteration % # call without argument % time {maybe} 10000 19 microseconds per iteration % # call without argument; space added % time {maybe } 10000 3 microseconds per iteration % # call without argument; semicolon added % time {maybe;} 10000 4 microseconds per iteration % complex script, call with arguments % time {maybe empty; maybe empty} 10000 7 microseconds per iteration % # complex script, call without arguments % time {maybe;maybe} 10000 7 microseconds per iterationThanks, I have updated the issue list above accordingly --RJMI revised your update; hope you agree with the revision. Saving your previous version here if you want to restore it - delete if not needed. -- MS Hmm, I was a bit tackled by the usage of the time command to test the performance. In normal calls the problem will not occur, since it truly depends on any extra character before or after the command name (also newline). So, practically the problem will not arise that often. But being aware of it is good. -- RJMDKF: This is a shimmering effect between command names and scripts.
RJM: Ah, the link to Performance on top of this page has been added by anybody. Fine, prior to creating this page, I did search the wiki page titles for the phrase "speed", not "performance". However, I believe this page might serve as a summarization of issues with significant speed impact and quite common usage in scripts.
RJM: Referencing to (4), examples follow that document the time command problem inside procs.
# "procless" loop % time {for {set n 0} {$n < 100000} {incr n} {set x $n}} 1378209 microseconds per iteration % proc a {} {for {set n 0} {$n < 100000} {incr n} {set x $n}} % time a 250852 microseconds per iteration % proc b {} {time {for {set n 0} {$n < 100000} {incr n} {set x $n}}} % b 1132636 microseconds per iteration