|
In this page I'd like to describe some of the more
interesting technical problems that I've
solved over the last few years. I hope you find some of these ideas helpful.
Tcl/Tk Style Guide
I wrote this Tcl/Tk Style Guide as a result of several
phenomena I've noticed in the industry:
-
Tcl/Tk codebases tend to "tip over" and become unmaintainable at the 10K - 20K
SLOC barrier.
-
Tcl/Tk is an easy language to get yourself in trouble. There are places
in the language where you are better off staying away from.
-
Conversely, there
are Tcl/Tk language features that should be a standard part of the arsenal;
they yield code that does scale to large projects.
Tcl/Tk Performance Techniques
-
Don't use the "-command" option in the lsort command.
This option will always result in a slow sorting operation. Actually,
that's rather well-known to Tcl programmers. But what you might not
realize is something a little more insidious: This option might only
become a big problem only when your program has to deal with real data.
(You know, when you do your initial testing, you typically do it on
small-sized data sets. But when you get closer to delivering production
code, you find that you have to deal with large, real-world data sets.
By that time it can be difficult to find a good alternative!)
Suggestion: Either use one of the other lsort options
(often the "-index" option will do what you want), or if that's not possible,
do the sorting directly in C or C++. And test your sorts early in the
development process to weed this problem out.
-
Don't use the "array names arrayName PATTERN" idiom.
It's not too surprising that this particular operation is linear on
the number of elements in the array. What is a bit surprising is how
easily this construction can negatively affect the performance of your
program.
This is kind of a shell game phenomenon. After all,
you may have used an associative array precisely because you wanted to
avoid the linear lookup that a Tcl list would have given you. The problem
is that you will immediately give back your performance gain if you
use the PATTERN when looking up elements in the array!
-
You may have to implement computationally intensive parts of your
program in C or C++. Again, this is no surprise. But it can be
rather hard to even identify which parts of the program are in fact
taking up the time. I suggest that you use either the
Tcl time command, or that you download
the Tcl profiler.
-
Be aware of the Tcl compiler. The speed of your program
usually will greatly depend upon writing code that the Tcl compiler will
optimize into bytecodes. The simple rule of thumb is to always
put braces around arguments to the if and
expr commands; e.g.:
set a [expr {($b + $c)/2}]
if {[meets_condition $d]} return
instead of
set a [expr (($b + $c)/2)]
if [meets_condition $d] return
-
Canvas widget.
The canvas widget traverses tags in linear time, not constant time.
This means that if you have a huge canvas with lots of tags,
you can quickly get into the soup, performance-wise.
If you're having canvas performance problems like this, you may
have to consider eschewing Tk canvas tags, and instead implement
them in your application using associative arrays.
-
Other techniques
can be found in the
Tk Performance or in the
Tcl Performance sections of
the Tcl Wiki website.
Tcl/Tk Coding Techniques
-
Use namespaces.
One of the biggest gripes about Tcl has always been its uncontrolled,
"scripty" nature. This is often viewed as a benefit when tossing together
a quick prototype--but is a decided weakness when fielding a large
production program.
The Tcl namespace feature helps to manage the maintenance
of a Tcl program, making programs in the language roughly as
maintainable as a C
programs. It does this by allowing the programmer to define modules that
contain the equivalent of static variables and procedures,
thus reducing the need to define variables and procedures in the
global scope. This in turn can considerably shrink the time needed to
locate and fix a program bug, since large portions of the program can
usually be ignored for any particular bug.
In order to use namespaces effectively, though, the Tcl program should
make use of the convention that all namespace variables should be
written only within a single file that defines that namespace.
Otherwise, the desirable "bug locality" of the Tcl program is lost, even
if namespaces are utilized.
-
Don't use "update" when "update idletasks" is what you want.
"update" will see if there are any events to process,
while "update idletasks" will just run any idle tasks waiting to run.
Typically, those idle tasks will cause outputs to be flushed to the screen,
though of course an idle task can do anything.
So, what's the difference really?
The difference is, your program is in control (pretty much anyway) if you
just use "update idletasks". But the user has control
if you use "update". That means that after an "update", you must test
the existence of any non-local resource, including
(especially) the existence of any window that your program uses. E.g.:
toplevel .t
# ...create the window children of .t
update
focus .t ;# UNSAFE, usually
The problem is that the user may have dismissed .t
during the update.
The same comment goes for the vwait command, too;
during vwait, events from the user will be processed.
-
Remember, native Tcl is not a threaded langauge.
Tcl is an event-driven language with a single stack.
While your program processes an event, it can use update
or vwait to suspend the event processing, allowing a
new event to be processed while the original event's context is pushed
onto the execution stack. But if the new event's execution is also
suspended (using, say, another vwait), the original
event's execution will not be restarted. It won't be restarted until
the new event's execution completes. Don't con yourself into believing that
your program has magically become threaded in cases like this!
-
If you expect your Tcl/Tk program to work on Windows and Unix, be prepared
to do some extra work.
The Windows implementation of Tk has historically not been as robust
as the Unix implementation--not surprisingly, since its roots are
entirely in Unix. Though the Windows implementation is getting better
and better, there are some areas that still must be dealt with.
First, you will probably have to code at least part of your
program in C or C++. This is especially true if you are making
a custom widget, or an extension to an existing widget. Typically in
these programs you will have a display function. I have never
seen a case where a widget display function will be fast
enough under Windows if it isn't coded in C or C++.
Second, you will probably have at least one window layout that you
should create once and withdraw when dismissed. This
is because Windows is notoriously slow in creating new subwindows.
|