Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

byte complied uplevel with single argument

4 views
Skip to first unread message

Tom Wilkason

unread,
Feb 28, 2002, 8:37:15 PM2/28/02
to
Looking at the Tcl sources: The uplevel (and eval) commands both take their arguments
and concatentate them together, then 'eval' them in a non-byte compiled way. I can
understand why no byte compiling is done for multiple arguments since the evalued
object is of a short duration, but in the case of a single argument I would think it
be prefereable to go ahead and byte complile the single argument. There is a good
chance this single object will be around a while. e.g. uplevel {set a 1}

As a test case I modifed the Tcl_UplevelObjCmd command to byte complile for a single
argument case (changed the flag to Tcl_EvalObjEx to 0) and ran a little test. I got a
4x speedup just by letting the code byte compile. Now in the case of uplevel, I
typically use single arguments (that is putting the code in braces) to prevent
flattening. I don't see the big advantage of this change for eval, since for me
anyway, its purpose in life is to flatten arguments.

This freebee sounds too good to be true. Does anyone see any gotchas in doing this?

PS. I ran the Tcl test suite and it passes fine.

proc timeit {args} {
set one 1
set two 2
stuff
#puts $c
}
proc stuff {args} {
uplevel {set c [expr {$one + $two}]}
}
# Note, this is with debug enabled
time {timeit} 1000
#1688 us per instruction if not byte compiled
#420 us per instruction if byte compiled

--
Tom Wilkason


Don Porter

unread,
Feb 28, 2002, 8:45:41 PM2/28/02
to
In article <fXAf8.16843$s03.6...@news2.east.cox.net>, Tom Wilkason wrote:
> Looking at the Tcl sources: The uplevel (and eval) commands both take their arguments
> and concatentate them together, then 'eval' them in a non-byte compiled way. I can
> understand why no byte compiling is done for multiple arguments since the evalued
> object is of a short duration, but in the case of a single argument I would think it
> be prefereable to go ahead and byte complile the single argument. There is a good
> chance this single object will be around a while. e.g. uplevel {set a 1}
...

> This freebee sounds too good to be true. Does anyone see any gotchas in doing this?

[uplevel] causes the evaluation to be in the callers context, where
[set] may resolve to something other than you think. (Caller may be in
another namespace). So, bytecompile of [uplevel] does not appear to be
possible.

--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|

Andreas Kupries

unread,
Mar 1, 2002, 3:15:12 AM3/1/02
to
"Tom Wilkason" <tom.wi...@cox.net> writes:

> Looking at the Tcl sources: The uplevel (and eval) commands both
> take their arguments and concatentate them together, then 'eval'
> them in a non-byte compiled way. I can understand why no byte
> compiling is done for multiple arguments since the evalued object is
> of a short duration, but in the case of a single argument I would
> think it be prefereable to go ahead and byte complile the single
> argument. There is a good chance this single object will be around a
> while. e.g. uplevel {set a 1}

> As a test case I modifed the Tcl_UplevelObjCmd command to byte
> complile for a single argument case (changed the flag to
> Tcl_EvalObjEx to 0) and ran a little test. I got a

When you say bytecompile, what was bytecompiled ?
The uplevel command itself, or the script it executes ?

In the latter case Don has already said that this is
problematic because the namespace context and such can be
different in the level the script is executed in, with
different definitions for set, etc.

--
Sincerely,
Andreas Kupries <akup...@shaw.ca>
Developer @ <http://www.activestate.com/>
Private <http://www.purl.org/NET/akupries/>
-------------------------------------------------------------------------------
}

Michael Schlenker

unread,
Mar 1, 2002, 10:24:18 AM3/1/02
to
Andreas Kupries wrote:

> "Tom Wilkason" <tom.wi...@cox.net> writes:
>>As a test case I modifed the Tcl_UplevelObjCmd command to byte
>>complile for a single argument case (changed the flag to
>>Tcl_EvalObjEx to 0) and ran a little test. I got a
>>
>
> When you say bytecompile, what was bytecompiled ?
> The uplevel command itself, or the script it executes ?
>
> In the latter case Don has already said that this is
> problematic because the namespace context and such can be
> different in the level the script is executed in, with
> different definitions for set, etc.
Maybe a test in the tcl testsuite should address this to illustrate the
problem?

Michael Schlenker

miguel sofer

unread,
Mar 1, 2002, 10:44:46 AM3/1/02
to

Excellent suggestion, thank you!

Bug logged at SF

http://sourceforge.net/tracker/index.php?func=detail&aid=524383&group_id=10894&atid=110894

Cheers
Miguel Sofer

Tom Wilkason

unread,
Mar 1, 2002, 9:06:40 PM3/1/02
to
"Andreas Kupries" <akup...@shaw.ca> wrote in message
news:87g03k9...@bluepeak.home...

| "Tom Wilkason" <tom.wi...@cox.net> writes:
|
| > Looking at the Tcl sources: The uplevel (and eval) commands both
| > take their arguments and concatentate them together, then 'eval'
| > them in a non-byte compiled way. I can understand why no byte
| > compiling is done for multiple arguments since the evalued object is
| > of a short duration, but in the case of a single argument I would
| > think it be prefereable to go ahead and byte complile the single
| > argument. There is a good chance this single object will be around a
| > while. e.g. uplevel {set a 1}
|
| > As a test case I modifed the Tcl_UplevelObjCmd command to byte
| > complile for a single argument case (changed the flag to
| > Tcl_EvalObjEx to 0) and ran a little test. I got a
|
| When you say bytecompile, what was bytecompiled ?
| The uplevel command itself, or the script it executes ?

The body of the script it executes (argument to uplevel) was byte compiled by passing
a 0 to the Tcl_EvalObjEx command (rather than TCL_EVAL_DIRECT) after the frame
context is changed (only for the case of a single argument). i.e. the latter case.

| In the latter case Don has already said that this is
| problematic because the namespace context and such can be
| different in the level the script is executed in, with
| different definitions for set, etc.

I see what you are saying, but doesn't the TclCompEvalObj routine (or a function it
calls) already handle these context changes automagically by appropriatly
invalidating the byte compilation? (see comment in TclCompEvalObj routine below).
/*
* Get the ByteCode from the object. If it exists, make sure it hasn't
* been invalidated by, e.g., someone redefining a command with a
* compile procedure (this might make the compiled code wrong). If
* necessary, convert the object to be a ByteCode object and compile it.
* Also, if the code was compiled in/for a different interpreter,
* or for a different namespace, or for the same namespace but
* with different name resolution rules, we recompile it.
*
* Precompiled objects, however, are immutable and therefore
* they are not recompiled, even if the epoch has changed.
*
* To be pedantically correct, we should also check that the
* originating procPtr is the same as the current context procPtr
* (assuming one exists at all - none for global level). This
* code is #def'ed out because [info body] was changed to never
* return a bytecode type object, which should obviate us from
* the extra checks here.
*/
I tried redefining the set command between calls of the routine that performs the
uplevel and everything still works ok (I also stepped through it in the debugger).

namespace eval ::test {


proc timeit {args} {
set one 1
set two 2

stuff ;# Inital call, forces a byte complile of {puts "stuff ....}
stuff ;# This one runs the code within the uplevel byte compiled
puts "one=$one" ;#sb 3
rename set _set
proc set {args} {
puts "dummy: $args"
}
stuff ;# This recompiles the bytecode, it was invalidated by ?proc set??
puts "one=$one" ;sb 5
rename set {} ;# cleanup
rename _set set
}
}
proc stuff {args} {
uplevel {
puts "stuffing $one $two"


set c [expr {$one + $two}]

set one $c
puts "one=c=$c"
}
}
::test::timeit
# Seems to work
Can someone come up with a test case where it won't work?.

--
Tom Wilkason


Don Porter

unread,
Mar 2, 2002, 2:06:23 PM3/2/02
to
>| In the latter case Don has already said that this is
>| problematic because the namespace context and such can be
>| different in the level the script is executed in, with
>| different definitions for set, etc.

Tom Wilkason wrote:
> I see what you are saying, but doesn't the TclCompEvalObj routine (or a
> function it calls) already handle these context changes automagically by
> appropriatly invalidating the byte compilation?

No, that's the solution to a different problem.

> I tried redefining the set command between calls of the routine that
> performs the uplevel and everything still works ok

Right. We are already protected if [::set] means one thing when we
compile, and something new when we come back to execute.

With [uplevel] though, [set] could mean [::foo::set] to one caller
and [::bar::set] to another, or just [::set] with most. If we
compile only one of those in, we'll be wrong when another is correct.

Now you could futher limit your compiling of [uplevel] to the case
where the command is fully qualified, but then you face the same
problem all over again with command substitutions and variable
substitutions in the arguments.

If you limit to the case where there are no substitutions left and all
commands and variables are fully qualified, then compiling is safe, but
[uplevel] is also completely unnecessary.

> Can someone come up with a test case where it won't work?.

Someone should, and then contribute it to Tcl Bug 524383.

R. T. Wurth

unread,
Mar 2, 2002, 9:03:11 PM3/2/02
to
In article <slrna828l...@clover.cam.nist.gov>, d...@email.nist.gov wrote:

[Some very good comments that are largely irrelevant to the point I'm
about to make, all of which I'm snipping.]

I would add one side note: not compiling uplevel bodies is not as
bad as it looks. I work on a project that wraps its code in a
wrapper that handles entry/exit logging with argument lists and
return codes (if entry/exit logging is enabled), catching, logging,
and re-throwing errors, and other useful things. (We use it as:
proc xxx {arg1 ... } {wrap {
#body goes here
}} ). The wrapper executes the code through uplevel, so essentially
all our code executes via uplevel. At one point the team grew quite
concerned about this, so I did some looking into the efficiency and
byte code compilation aspects. Using byte code compiler tracing
(enabled, I think by setting some variable somewhere), I discovered
that, yes, none of the commands executed by uplevel were being byte
code compiled, BUT, the bodies of contained loops were getting
byte-code compiled, once per proc invocation. So, while there was
some inefficiency, it wasn't as bad as we thought.

More recently, I have had occasion to profile code (using the
excellent profiling tool in Tclx--I can't say enough good things
about it), I did find typically between 3 and 5 procedures per
application (out of between 20 and 40 procs per application) where
the wrapper was turning out to be a significant user of CPU cycles.
The typical "problem" proc was straight-through code, with no loops
or decisions and had 10 or fewer lines. Also, the typical problem
routine tended to be called very frequently, such as once per record
in a very large database table.

So, I conclude my profiling evidence is consistent with and
explainable by my earlier byte-code compilation trace study, namely
that the lack of byte-code compilation for [uplevel] in this
particular code-wrapping situation was not a problem for procs that
spend most of their time in loops, but it is a problem for procs that
don't contain loops.
--
R. T. Wurth / rwu...@att.net / Rumson, NJ USA
Consultant to the telecommunications industry

0 new messages