Reputation: 2821
I have a proc which gets executed a couple of hundred thousand times:
proc ::TsaiWu { x_all_comp x_all_tens y_all_comp y_all_tens s_allow x_stress y_stress s_stress } {
set FX [ expr { 1 / $x_all_tens - 1 / $x_all_tens } ]
set FY [ expr { 1 / $y_all_tens - 1 / $y_all_tens } ]
set FXX [ expr { 1 / ( $x_all_tens * $x_all_comp ) } ]
set FYY [ expr { 1 / ( $y_all_tens * $y_all_comp ) } ]
set IXY [ expr { -0.5 * sqrt( ( $y_all_tens * $y_all_comp) / ( $x_all_tens * $x_all_comp) ) } ]
set FXY [ expr { $IXY / sqrt( $x_all_tens * $x_all_comp * $y_all_tens * $y_all_comp ) } ]
set FSS [ expr { 1 / pow($s_allow,2) } ]
set a [ expr { $FXX * pow($x_stress,2) + $FYY * pow($y_stress,2) + 2 * $FXY * $x_stress * $y_stress + $FSS * pow($s_stress,2) } ]
set b [ expr { $FX * $x_stress + $FY * $y_stress } ]
set TW [ expr { $a + $b } ]
set SR [ expr { ( -2 + sqrt( pow($b,2) + 4 * $a) ) / ( 2 * $a ) } ]
set result [ list $TW $SR ]
return $result
}
What possibilties do I have to speed it up? I have added curly brackets to the expression, that did help a lot.. Is it faster to rewrite the single equations into just two (one for TW and one for SR) ? Then I would only need to call expr twice. Would it be faster if i wrote this piece of code in c++ and somehow use in my tcl script? Is there a speed difference between sqrt and pow(x,0.5) ? Any tips on how to speed this proc upp are welcome! In the final version i will use upvar or a global variable so I dont't have to hand over the result as a list copy (is it much slower?) Would the code run faster if I would not put these lines in a proc by itselv, and insted place it "inline" the code where it is needed? As far as I know procs are faster in TCl...
Upvotes: 0
Views: 679
Reputation: 137637
Provided you brace your expressions (important!) and use just local variables, it makes very little difference. The cost of reading and writing a local variable is minimal, not much more than reading and writing a variable in C. The real costs of execution will mainly be in the arithmetic operations and the function calls; it is those that you should minimize (while retaining correctness, of course).
Specific issues: You might find that using $b**2
or $b*$b
is cheaper than pow($b,2)
. The sqrt()
calls are likely to be expensive; can you reduce the number that you do? (Note that pow(…,0.5)
is unlikely to be better, and might possibly be worse in terms of numerical accuracy.)
Generic issues: Changing the build of Tcl can have quite a big effect. I found a few weeks ago that switching from the system build of 8.5.9 to my own (made with an up-to-date LLVM/Clang) made a substantial improvement to speeds, yet with the identical source code and identical scripts. Funny how these things go…
If you're really hurting for time, try writing a little C code to implement the math. That will probably be faster (and will allow you to use float
instead of being forced to use double
, which is what Tcl uses internally for floating-point numbers) but I don't know how much faster. If you're doing this, you'll find the API calls Tcl_GetDoubleFromObj
and Tcl_NewDoubleObj
to be useful.
Upvotes: 2
Reputation: 21
If you use tcl::unsupported::disassemble to examine the bytecode of the proc, you will see that one [expr] vs many [expr] makes no difference at all.
When you [time] make sure to test a large number of iterations so that you measure the run time, and not the compile-to-bytecode time which happens only once.
Your concern about "list copy" on return is unfounded. That's not how Tcl works. Toying with [upvar] to solve a "problem" you do not actually have is not a path you want to take.
Using a global variable will slow your proc down, possibly measurably. You want your [expr]essions in a proc so they can operate on local vars, where the speed gains of bytecode are the greatest.
Upvotes: 2