1

This is something of a general question. Assuming TCL 8.6, let's ay that I have a rather short procedure. I have 2 ways of returning the value of interest:
1. use some standard practice if/else code, store in a variable, and return the variable's value. For example:

proc me { gop goo } {
   if { [ lomsa $gop ] {
      set ret [ foo $goo $gop ]
   } else {
      set ret [ bar $gop $goo ]
   }
   return $ret
}

2. use the ternary argument, and basically, have the procedure no added private variable (that is, only the arguments are used). The output of ternary expression is what returns the value. For example:

proc me { gop goo } {
   expr { [ lomsa $gop ] ? [ foo $goo $gop ] : [ bar $gop $goo ] }
}

Readability is considered by some members of my team to be better slightly better in item 1.
I can't access the pseudo code engine in my TCL setup (it is a shell from a vendor), but I assume that difference in the code, and its' performance will be, but only slight, if at all. I.e., the procedure needs to store the value returned somewhere. How much is the cost of registering a specific variable for it, vs. just keeping it as a return value?
The question can be extended, for example, for switch statements. Same rules apply. The switch statement can store in a variable, and then, after the switch, the variable's value is returned, or the switch statement will just return the value, w/o storing it in a variable. Also, there can be extensive code prior to the return part of it. The listed above procedure is what they call a "convenience procedure"
You can assume that performance is of high interest for the code.

user1134991
  • 3,003
  • 2
  • 25
  • 35

4 Answers4

2

When in doubt, don't guess; time it!

For your example code, the difference in performance is within a couple of percent for the most trivial implementations of lomsa, foo and bar that I can get away with (and under the understanding that there's some minor technical differences in a few edge cases). For anything more complex, you're down in the noise as you are balancing the cost of local variable accesses with an extra call to the opcode that converts values to numeric values if possible, and exactly what is slower there is "it depends" territory.

The switch statement, when it is dealing with cases it can compile at all, uses the Tcl evaluation stack as temporary storage. Were I to write the bytecode compilation again, I'd probably use a temporary local variable (they're nameless; you can't touch them from your code) so as to keep some of the other calculations simpler. To know which is the fastest option, time your actual code; timing some proxy code that is less complex can easily give contrary conclusions.

In the future, the differences should matter even less. We're working on a way to compile to efficient native code. The compilation strategy we've developed pretty much eliminates the differences that you're seeing above even before we get into looking at actual code generation.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
1

Compiled to byte code, the two versions of the command aren't very much different: in both cases the selection structure is translated to branch instructions.

To see roughly what your code will look like compiled and ready to run, use tcl::unsupported::disassemble proc <name-of-proc>. Some caveats: the command is unsupported, meaning it isn't guaranteed to work the way you expect it to or to keep working the way it does now in future releases. Seeing the disassembly list can also be much more misleading that you might think, unless you are very familiar with how the byte-code interpreter works.

To find out more about performance, you should instead look at the time command and the bench package, which are very useful for measuring performance.

Documentation: bench (package), time

Peter Lewerin
  • 13,140
  • 1
  • 24
  • 27
1

Aside from readability, the ternary operator may produce unexpected results. For example, if the selected proc returns 0123, your me proc will return 83 instead. So unless you are sure that situation can never occur, it's safer to use the if command.

Schelte Bron
  • 4,113
  • 1
  • 7
  • 13
0

What about the middle ground?

proc me { gop goo } {
   if { [lomsa $gop] } {
      foo $goo $gop
   } else {
      bar $gop $goo
   }
}

or if you like an explicit return:

proc me { gop goo } {
   if { [lomsa $gop] } {
      return [foo $goo $gop]
   } else {
      return [bar $gop $goo]
   }
}
glenn jackman
  • 238,783
  • 38
  • 220
  • 352