Home > Archive > Tcl > October 2005 > Number of arguments and performance
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Number of arguments and performance
|
|
| tunity5@yahoo.com 2005-10-22, 6:58 pm |
| Dear all,
I have the following proc's defined (Tcl/Tk 8.4.11 on Windows XP):
proc do_nothing_significant_1 {} {
set a "nothing here"
return $a
}
proc do_nothing_significant_2 {input} {
set a "nothing here"
return $a
}
set short "a short string"
set long "a string that is around 100 characters long......"
Now, when I test these, I get interesting timings:
% time {do_nothing_significant_1} 1
consistently gets 13/14 microseconds per iteration. In constrast,
either of these
% time {do_nothing_significant_2 $long} 1
% time {do_nothing_significant_2 $short} 1
consistently gets 18/19/20 microseconds per iteration. The length of
the input argument does not seem to be a factor.
I experimented with adding a second argument into the picture.
Regardless, it seems that adding an argument degrades the performance
by about 30%.
Is this a consistent with other people's experience? Is there
something I can do to improve the performance?
| |
| Bryan Oakley 2005-10-22, 6:58 pm |
| tunity5@yahoo.com wrote:
> I experimented with adding a second argument into the picture.
> Regardless, it seems that adding an argument degrades the performance
> by about 30%.
>
> Is this a consistent with other people's experience? Is there
> something I can do to improve the performance?
>
Have you identified that this is the bottleneck in your application, or
is this purely an academic exercise? I think it's safe to say there is
no way to speed up your programs by simply adjusting how many arguments
your procs have.
There are a lot of factors at play here, and I doubt that your
observation of a 30% degredation by adding an unused argument at any way
affects real world code.
For example, you claim that running your proc once takes 18-20
microseconds, and running the same proc with one argument takes 30%
longer time. If your application only calls that proc once, I hardly see
how 6 microseconds is going to be an issue.
If you're calling this procedure tens of thousands of times (the only
time when a few microseconds will make a noticible difference), why are
you doing a test with just a single iteration? Run the same test on my
machine with 1000 iterations shows maybe a 5% difference, and we're
talking fractions of a microsecond.
Also, you might be interested in seeing the differences in timing when
you put the call to your functions inside a proc, thus taking advantage
of byte code compilation (which better reflects how most real-world code
is executed):
proc foo_1 {} {
do_nothing_significant_1
}
proc foo_2 {} {
do_nothing_significant_2 "a short string"
}
On my box, calling foo_1 takes virtually exactly as long as calling
foo_2, and in some cases foo_2 actually runs faster.
So I wouldn't get too concerned about a perceived inefficiency in adding
arguments to procs. It's not an issue.
| |
| miguel sofer 2005-10-22, 6:58 pm |
| Please do read Bryan's answer - "do not worry about this" is the best
advice. I will just provide a few secondary comments and info
Well - the arguments have to get their values befor the proc is run,
and variables have to be read, ...
Do not time single iterations of small scripts - too much noise. My
timings (done under Tcl8.5 so that we get better resolution) shows that
your 30% estimate is not correct.
% time {do_nothing_significant_1} 100000
3.16944 microseconds per iteration
% time {do_nothing_significant_2 123} 100000
3.162 microseconds per iteration
% time {do_nothing_significant_2 1234567890987654321} 100000
3.15253 microseconds per iteration
% set a 123
123
% time {do_nothing_significant_2 $a} 100000
3.83421 microseconds per iteration
% set a [string repeat 0 1000000]; puts {}
% time {do_nothing_significant_2 $a} 100000
3.94556 microseconds per iteration
Remark that Tcl's [time] measures wall clock time, and so is dpendent
on external factors such as the current machine load. Given that, you
can conclude from the above:
* a single argument has an unmeasurably small on proc invocation
timings
* the length of the input argument has no impact
* reading a variable's value seems to cost as much as 30% of a command
invocation. This is much smaller if you are reading a local variable in
a proc body:
% proc test {} {
set a [string repeat 0 1000000]
puts 1:[time {do_nothing_significant_1} 100000]
puts 2:[time {do_nothing_significant_2 $a} 100000]
}
% test
1:3.16736 microseconds per iteration
2:3.44978 microseconds per iteration
| |
| tunity5@yahoo.com 2005-10-22, 6:58 pm |
| Bryan Oakley wrote:
> Have you identified that this is the bottleneck in your application, or
> is this purely an academic exercise?
First, I should mention that we are quite happy with the performance as
it stands, it is just that I was quite surprised when I saw this
effect.
This proc is called within a loop of tens of thousands of iterations,
mostly around 100,000 times. I agree with your point and we have been
quite pragmatic so far with respect to bottlenecks and the potential
bottlenecks. This is something that would make many users happy if
improved.
> If you're calling this procedure tens of thousands of times (the only
> time when a few microseconds will make a noticible difference), why are
> you doing a test with just a single iteration? Run the same test on my
> machine with 1000 iterations shows maybe a 5% difference, and we're
> talking fractions of a microsecond.
As I mentioned above, there is a main loop, and this proc gets called
inside that main loop, among other procs. Perhaps I need to read up on
time. If the argument to time is not called consecutively in actual
code, would I still see the benefit of using a larger count for time?
I thought not; so that is why I used 1 as a count. But I am aware that
as I increase the count, the time it takes per iteration decreases; but
the difference still remains and is significant as a percentage
(25-30%) and perhaps means nothing:
% time {do_nothing_significant_1} 1000
1.908 microseconds per iteration
% time {do_nothing_significant_2 $long} 1000
2.542 microseconds per iteration
% time {do_nothing_significant_2 $short} 1000
2.582 microseconds per iteration
> Also, you might be interested in seeing the differences in timing when
> you put the call to your functions inside a proc,
We thought about inlining some of the code but decided against it. The
code readability/maintainability is more important at this point.
| |
| tunity5@yahoo.com 2005-10-22, 6:58 pm |
| miguel sofer wrote:
> * reading a variable's value seems to cost as much as 30% of a command
> invocation. This is much smaller if you are reading a local variable in
> a proc body:
That is it! This makes a lot of sense and describes where the penalty
is coming from. It is consistent with the numbers that I am getting.
| |
| Gerald W. Lester 2005-10-22, 6:58 pm |
| tunity5@yahoo.com wrote:
> Dear all,
>
> I have the following proc's defined (Tcl/Tk 8.4.11 on Windows XP):
>
> proc do_nothing_significant_1 {} {
> set a "nothing here"
> return $a
> }
>
> proc do_nothing_significant_2 {input} {
> set a "nothing here"
> return $a
> }
>
> set short "a short string"
> set long "a string that is around 100 characters long......"
>
> Now, when I test these, I get interesting timings:
>
> % time {do_nothing_significant_1} 1
>
> consistently gets 13/14 microseconds per iteration. In constrast,
> either of these
>
> % time {do_nothing_significant_2 $long} 1
> % time {do_nothing_significant_2 $short} 1
>
> consistently gets 18/19/20 microseconds per iteration. The length of
> the input argument does not seem to be a factor.
>
> I experimented with adding a second argument into the picture.
> Regardless, it seems that adding an argument degrades the performance
> by about 30%.
>
> Is this a consistent with other people's experience? Is there
> something I can do to improve the performance?
>
No, because you are not seeing a performance issue, but rather a factor in
incorrect timing test.
First, you should call each routine from the command line once before doing
a timing test on it, this causes it to compile.
Second you need to run a timing test with a number much larger then 1, so
that random interrupts don't over shadow your measurements.
Here is my run of your test cases (pasted after procs and sets done):
% do_nothing_significant_1
nothing here
% do_nothing_significant_2 a
nothing here
% time {do_nothing_significant_1} 1000
7.74 microseconds per iteration
% time {do_nothing_significant_2 $long} 1000
2.78 microseconds per iteration
% time {do_nothing_significant_2 $short} 1000
2.911 microseconds per iteration
--
+--------------------------------+---------------------------------------+
| Gerald W. Lester |
|"The man who fights for his ideals is the man who is alive." - Cervantes|
+------------------------------------------------------------------------+
|
|
|
|
|