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

Why is tcl::mathfunc::min over 4 times slower than a foreach loop testing each value in the args list?

112 views
Skip to first unread message

jda...@gmail.com

unread,
Jul 5, 2017, 4:56:55 PM7/5/17
to
I was looking for the max and min of a list of numbers, using a foreach loop, and thought to try using the tcl::mathfunc functions instead. however they were much slower than the foreach loop.

[info body ::tcl::mathfunc::min]
shows that each argument is explicitly checked for non-numbers using:

# this will handle forcing the numeric value without
# ruining the internal type of a numeric object
if {[catch {expr {double($arg)}} err]} {
return -code error $err
}

I find the comment confusing. It appears that the test insures an error if a non-numeric argument is supplied.

This makes tcl::mathfunc::min almost 5 times slower on my computer than a loop without the explicit test.

TIP (#255) that added max and min does not mention the test.

Does anyone have a feel for the downsides to using a locally defined min or max function that leave this test out or tests only the result? I've tried a few corner cases with arguments including . and Inf, but none seem to cause any unexpected trouble so far.

Dave B

Arjen Markus

unread,
Jul 6, 2017, 3:01:45 AM7/6/17
to
You could try to put the catch around the foreach instead of inside. And I suggest you make an error report for this.

The implementation does seem inefficient but I have not tried timing it myself.

Regards,

Arjen

jda...@gmail.com

unread,
Jul 6, 2017, 12:12:16 PM7/6/17
to
I do not see an error, but there is certainly room for improvement.

Putting the whole inner loop within a [try] or [catch] cut time by about 20%
The test [expr {double($val)}] takes a lot more time than the [if] that accumulates the minimum value.

Using a 1000 entry list, timings with different versions of the inner loop are:
127 test only
150 test and if
36 if only


A faster test for "can this object be a number" would clearly speed up this function, and might be more generally useful. I'm going to look through tcl::unsupported::*, and the internals to see what is available.

Not explicitly testing for numeric values in [min] and similar functions, and letting the normal TCL error handling catch silly values, might work as well. I'm going to look for cases where that does not work well.

Dave B

Donal K. Fellows

unread,
Jul 6, 2017, 5:13:16 PM7/6/17
to
On 06/07/2017 17:12, jda...@gmail.com wrote:
> A faster test for "can this object be a number" would clearly speed
> up this function, and might be more generally useful. I'm going to
> look through tcl::unsupported::*, and the internals to see what is
> available.

The operation we've got is [string is double -strict], which is public
but perhaps a little non-obvious. That compiles to efficient bytecode.

Donal.
--
Donal Fellows — Tcl user, Tcl maintainer, TIP editor.

Ralf Fassel

unread,
Jul 7, 2017, 8:27:15 AM7/7/17
to
* "Donal K. Fellows" <donal.k...@manchester.ac.uk>
| On 06/07/2017 17:12, jda...@gmail.com wrote:
| > A faster test for "can this object be a number" would clearly speed
| > up this function, and might be more generally useful. I'm going to
| > look through tcl::unsupported::*, and the internals to see what is
| > available.
>
| The operation we've got is [string is double -strict], which is public
| but perhaps a little non-obvious. That compiles to efficient bytecode.

proc dexpr {val} { catch {expr {double($val)}} }
proc sexpr {val} { string is double -strict $val }

For numbers the difference is small:
% time {dexpr 1.0} 10000000
0.3145418 microseconds per iteration
% time {dexpr 1.0} 10000000
0.3133542 microseconds per iteration

% time {sexpr 1.0} 10000000
0.3403064 microseconds per iteration
% time {sexpr 1.0} 10000000
0.3283447 microseconds per iteration

For non-numbers the difference is huge in favor of 'string is'

% time {dexpr foo} 10000000
2.3977025 microseconds per iteration
% time {dexpr foo} 1000000
2.508554 microseconds per iteration

% time {sexpr foo} 10000000
0.359159 microseconds per iteration
% time {sexpr foo} 10000000
0.3626173 microseconds per iteration

but since a non-number would only raise an error this will probably not
speed up the original problem...

R'

jda...@gmail.com

unread,
Jul 7, 2017, 11:11:44 AM7/7/17
to
Replacing the [catch {expr {double($arg)}} err]
with ![string is double -strict $val]
makes min about 4 times faster on my 1000 element list test case.
That is still about 2 times slower than with no test, but much better than it was.

Dave B
0 new messages