views:

457

answers:

6

I came across the following code recently and would like to optimize it:

Public Shared Function ComputeLabel(ByVal Action As Integer, ByVal Flag1 As Boolean, ByVal Flag2 As Boolean) As String
   Dim Prefix As String = ""

   If Flag2 Then
      Prefix = "Conditional "
   ElseIf Flag1 Then
      Prefix = "Held "
   End If

   Select Case Action
      Case 0
         Return ""
      Case 1
         Return Prefix & "Cancelled"
      Case 2
         Return Prefix & "Discontinued"
      Case 3
         Return Prefix & "Suspended"
      Case 4
         Return Prefix & "Unsuspended"
      Case 6
         Return Prefix & "Collected"
      Case 7
         Return Prefix & "Released from Hold"
      Case 8
         Return Prefix & "Modified"
      Case 9
         Return Prefix & "Discontinued for the Future"
      Case 10
         Return Prefix & "Verified"
      Case 11
         Return Prefix & "Modified And Verified"
      Case 12
         Return "Hold " & Prefix & "Cancelled"
      Case Else
         Return ""
   End Select
End Function

Note that Action 0 is the most common case.

Okay, I've already cleaned up this function a bit--it was using a variable and returning it at the end, and using Return seems better. But additionally, I think it would be better code to build an array at the beginning of the report execution, and then just access array elements each time this function is called, instead of using a Select statement. But case 12 is making things more complicated (as you can see, it adds the prefix in the middle instead of at the beginning.)

What do you think would be the best way:

  • One time building a 39-element array for the three cases:

    Private Shared OrderActions() As String = {"", "Cancelled", ...}
    

    Then in the function accessing it like so:

    If Action < 0 OrElse Action >= 13 Then Return ""
    Return OrderActions(Action - Flag2 * 13 - (Flag1 AndAlso Not Flag2) * 26)
    
  • Using a 13-element array with a Replace (something like Return Replace(LabelList(Action), "{Prefix}", Prefix)?)

  • Using a 12-element array with a special case for Action 12.

  • Something else I haven't thought of.

?

Update 1: my formatting was off so the options might have been unclear. It should be more readable now.

Update 2: I see what you mean that from a performance perspective, fully expanding all the cases and using simple variable assignment is probably fastest. So... let's say top speed is not the priority, but overall elegance is (a combination of clean code and speed). Any chance people could give their take on that, too? I'll vote everyone up who gives reasonable help for all aspects of the question.

Update 3: One additional consideration I was ignoring is that some non-experienced programmers are going to be maintaining this long-term, so it does need to be easy to understand. I guess my examples of trying to shorten the code really aren't good from this perspective.

Update 4: TESTING IS KING OVER ALL!!! Once I was inspired to do some speed tests, I got some interesting results. See my answer below.

+1  A: 

You can't optimize it, it is already very efficient. You can make it more readable though, Flag1 and Flag2 definitely ought to be renamed to Held and Conditional.

Hans Passant
Thank you... in the original code they are named appropriately. I obfuscated some things. Sorry to have thrown a wrench in it. Your name isn't Wayne, is it?
Emtucifor
+2  A: 

Well if this is a bottleneck and speed is a priority then my first suggestion is to eliminate the string concatenation by duplicating much of your logic. This will make the function bigger and less readable, but maybe that's worth it for the speed...

Public Shared Function ComputeLabel(ByVal Action As Integer, ByVal Flag1 As Boolean, ByVal Flag2 As Boolean) As String
   Select Case nHVCOrderAction
        Case 0
            Return ""
        Case 1
            If Flag2 Then
                Return "Conditional Cancelled"
            ElseIf Flag1 Then
                Return "Held Cancelled"
            Else
                Return "Cancelled"
            End If
        Case 2
            If Flag2 Then
                Return "Conditional Discontinued"
            ElseIf Flag1 Then
                Return "Held Discontinued"
            Else
                Return "Discontinued"
            End If
        ' And so on...
   End Select
End Function

Relatively speaking the string concatenation in your code is probably going to be slower than the integer and boolean comparison operations you have here. So with enough repetition you should see a noticeable improvement in speed with this method.

UPDATE:

I wrote a quick & dirty VB.NET console app to test your original code vs mine. Here's the loop I ran for each (I didn't write every possible combination but you get the idea):

    Dim sw As New Stopwatch()
    sw.Start()

    For i As Integer = 0 To 10000000
        str = ComputeLabel(0, True, False)
        str = ComputeLabel(1, False, False)
        str = ComputeLabel(0, False, False)
        str = ComputeLabel(2, False, False)
        str = ComputeLabel(1, False, True)
        str = ComputeLabel(2, True, True)
        str = ComputeLabel(4, False, True)
        str = ComputeLabel(7, True, True)
        str = ComputeLabel(12, False, True)
    Next

    sw.Stop()
    Console.WriteLine(sw.ElapsedMilliseconds & " ms")

And here are the times:

Old method: 6189 ms

New method: 1374 ms

So there's a 5X speed improvement by removing the string concatenation and expanding the conditions for each case. Of course, as you may have noticed that loop runs 10 million times... that's a lot.

UPDATE:

I wrote another benchmark app to mimic your own exactly:

Module Module1

        Sub Main()

            Dim str As String = ""
            Dim sw As New Stopwatch()

            ' Test 1
            sw.Start()
            For i As Integer = 0 To 100000
                 For j As Integer = -1 To 13
                        str = ComputeLabel_X(j, False, False)
                        str = ComputeLabel_X(j, True, False)
                        str = ComputeLabel_X(j, False, True)
                 Next
            Next
            sw.Stop()
            Console.WriteLine("Old method: " & sw.ElapsedMilliseconds & " ms")
            sw.Reset()

            ' Test 2
            sw.Start()
            For i As Integer = 0 To 100000
                 For j As Integer = -1 To 13
                        str = ComputeLabel_Y(j, False, False)
                        str = ComputeLabel_Y(j, True, False)
                        str = ComputeLabel_Y(j, False, True)
                 Next
            Next
            sw.Stop()
            Console.WriteLine("New method: " & sw.ElapsedMilliseconds & " ms")
            sw.Reset()

        End Sub

        Public Function ComputeLabel_X(ByVal Action As Integer, ByVal Held As Boolean, ByVal Conditional As Boolean) As String
             Dim Prefix As String = ""

             If Conditional Then
                    Prefix = "Conditional "
             ElseIf Held Then
                    Prefix = "Held "
             End If

             Select Case Action
                    Case 0
                         Return ""
                    Case 1
                         Return Prefix & "Cancelled"
                    Case 2
                         Return Prefix & "Discontinued"
                    Case 3
                         Return Prefix & "Suspended"
                    Case 4
                         Return Prefix & "Unsuspended"
                    Case 6
                         Return Prefix & "Collected"
                    Case 7
                         Return Prefix & "Released from Hold"
                    Case 8
                         Return Prefix & "Modified"
                    Case 9
                         Return Prefix & "Discontinued for the Future"
                    Case 10
                         Return Prefix & "Verified"
                    Case 11
                         Return Prefix & "Modified And Verified"
                    Case 12
                         Return "Hold " & Prefix & "Cancelled"
                    Case Else
                         Return ""
             End Select
        End Function

        Public Function ComputeLabel_Y(ByVal Action As Integer, ByVal Held As Boolean, ByVal Conditional As Boolean) As String
             Select Case Action
                    Case 0
                        Return ""
                    Case 1
                        If Conditional Then
                            Return "Conditional Cancelled"
                        ElseIf Held Then
                            Return "Held Cancelled"
                        Else
                            Return "Cancelled"
                        End If
                    Case 2
                        If Conditional Then
                            Return "Conditional Discontinued"
                        ElseIf Held Then
                            Return "Held Discontinued"
                        Else
                            Return "Discontinued"
                        End If
                    Case 3
                        If Conditional Then
                            Return "Conditional Suspended"
                        ElseIf Held Then
                            Return "Held Suspended"
                        Else
                            Return "Suspended"
                        End If
                    Case 4
                        If Conditional Then
                            Return "Conditional Unsuspended"
                        ElseIf Held Then
                            Return "Held Unsuspended"
                        Else
                            Return "Unsuspended"
                        End If
                    Case 6
                        If Conditional Then
                            Return "Conditional Collected"
                        ElseIf Held Then
                            Return "Held Collected"
                        Else
                            Return "Collected"
                        End If
                    Case 7
                        If Conditional Then
                            Return "Conditional Released from Hold"
                        ElseIf Held Then
                            Return "Held Released from Hold"
                        Else
                            Return "Released from Hold"
                        End If
                    Case 8
                        If Conditional Then
                            Return "Conditional Modified"
                        ElseIf Held Then
                            Return "Held Modified"
                        Else
                            Return "Modified"
                        End If
                    Case 9
                        If Conditional Then
                            Return "Conditional Discontinued for the Future"
                        ElseIf Held Then
                            Return "Held Discontinued for the Future"
                        Else
                            Return "Discontinued for the Future"
                        End If
                    Case 10
                        If Conditional Then
                            Return "Conditional Verified"
                        ElseIf Held Then
                            Return "Held Verified"
                        Else
                            Return "Verified"
                        End If
                    Case 11
                        If Conditional Then
                            Return "Conditional Modified And Verified"
                        ElseIf Held Then
                            Return "Held Modified And Verified"
                        Else
                            Return "Modified And Verified"
                        End If
                    Case 12
                        If Conditional Then
                            Return "Hold Conditional Cancelled"
                        ElseIf Held Then
                            Return "Hold Held Cancelled"
                        Else
                            Return "Hold Cancelled"
                        End If
                    Case Else
                        Return ""
             End Select
        End Function
End Module

And my results are again consistently much faster with my code:

Old method: 169 ms

New method: 30 ms

I am of course running without debugging (Ctrl F5). And now I'm on an 3.0 GHz AMD quad core.

Steve Wortham
Hmm, thanks for responding. Speed is not such an issue that expanding all the way out like this is that useful. I always like to have speed in mind, but not to the point of distorting code when it isn't required. Do you have some sample code or a link for doing speed tests like this?
Emtucifor
Well I actually think your original code is the most elegant solution. Mine is... long, but effective. About the benchmarking: I just added the StopWatch Start/Stop code to my example above. I just prefer to create a Console Application project in Visual Studio to do this as it's a ridiculously easy way to test algorithms like this.
Steve Wortham
Thanks a lot for your help. Would you mind critiquing the new answer I posted?
Emtucifor
Too bad I can't give credit for shared answers. You engaged with me the most and I have no way to reward that. But, it looks like an array lookup is faster than a case statement.
Emtucifor
It's all good. I was an obsessive optimizer in my OpenGL days but sometimes I don't get to do enough of it. I'm glad to have helped.
Steve Wortham
The array lookup does, in the final analysis, appear to be fastest.
Emtucifor
@Emtucifor - Cool, it was a close one. It's gotta feel good to improve performance 5X over though. ;)
Steve Wortham
A: 

Steve Wortham already said the most important thing: the only inefficient part in your function is the string concatenation. If you really need that function to be fast, disregard source code length and expand every case explicitly, so you can avoid concatenating strings.

I would like to add one thing: Using an array won't help you optimize the function. .NET arrays are not C arrays, they are complex objects that must be constructed, filled with data and destroyed.

Eduardo León
The idea was that as expensive as building the array and killing it would be, this would only be done once at instantiation time (the Code section of an SSRS report is a static class) and be offset by the speed of the 287 calls to the function that simply pull out the string from the array.
Emtucifor
I have to disagree with this. In my post, I used arrays in the method that Emtucifor describes and the execution time was less than that of Steve Wortham's post.
Russ Bradberry
It turns out in the long run that I was too hasty to vote this post up. Apparently, it is factually incorrect, even though I appreciate the participation.
Emtucifor
A: 

Any thoughts on this version?

Public Shared Function ComputeLabel(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
   Dim Status As String = ""

   Select Case Action
      Case 0
         Return ""
      Case 1
         Status = "Cancelled"
      Case 2
         Status = "Discontinued"
      Case 3
         Status = "Suspended"
      Case 4
         Status = "Unsuspended"
      Case 6
         Status = "Collected"
      Case 7
         Status = "Released from Hold"
      Case 8
         Status = "Modified"
      Case 9
         Status = "Discontinued for the Future"
      Case 10
         Status = "Verified"
      Case 11
         Status = "Modified And Verified"
      Case 12
         If IsConditional Then Return "Hold Conditional Cancelled"
         If IsHeld Then Return "Hold Held Cancelled"
         Return "Hold Cancelled"
      Case Else
         Return ""
   End Select
   If IsConditional Then Return "Conditional " & Status
   If IsHeld Then Return "Held " & Status
   Return Status
End Function

or this?

Public Shared Function ComputeLabel(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
   Dim Status As String = ""

   Select Case Action
      Case 0, 5
         Return ""
      Case 1 To 11
         Status = Choose(Action, "Cancelled", "Discontinued", "Suspended", _
         "Unsuspended", "Collected", "Released from Hold", "Modified", _
         "Discontinued for the Future", "Verified", "Modified And Verified")
      Case 12
         If IsConditional Then
            Return "Hold Conditional Cancelled"
         ElseIf IsHeld Then
            Return "Hold Held Cancelled"
         Else
            Return "Hold Cancelled"
         End If
      Case Else
         Return ""
   End Select
   If IsConditional Then Return "Conditional " & Status
   If IsHeld Then Return "Held " & Status
   Return Status
End Function

I suspect the Choose function might be really slow...

And I guess the original function will still probably be clearer to the people maintaining the code than any of these other options. Sigh.

Emtucifor
I don't think your second snippet buys you much in either readabilty or speed. But I do like the first code snippet. It should be marginally faster since you said case 0 is most common, and you've moved the code around in a way to better optimize for that. And yet it's still intelligible.
Steve Wortham
Thanks for the thoughts!
Emtucifor
By the way, the Choose function is REALLY slow.
Emtucifor
+1  A: 
Russ Bradberry
Thanks for jumping in, Russ. Note that there should be 3 sets of labels, since the flags can be both false.Do you have any thoughts on whether the way you did it is faster or `_ConditionalLabels = {"1", "2", "3"}` is faster?
Emtucifor
I would make 3 sets of arrays to keep the amount, in each, generally small. But give a multidimensional array a shot and see what the performance is like. It may be negligible.
Russ Bradberry
Russ, I was asking about how the array is populated, using { } all at once or assigning each member as you did. Also, from what I've read recently, a multidimensional array (either kind) will be slower than a flat one with index calculation. Also, why does the number of items in the array have anything to do with speed of dereferencing an index into it?
Emtucifor
Ah, I like this approach too. It should be very close in performance to mine once you add that third condition and array to it.
Steve Wortham
It looks like an array dereference is faster than a case statement...
Emtucifor
One final thing: your code doesn't do bounds checking to handle the "Else" condition. I guess I'll be testing some speeds myself!
Emtucifor
yeah, I would check action first to see if it is > 12 or < 0 then assign it to 0 if it is.
Russ Bradberry
from a readbility standpoint, I would stay away from assigning them all at once using `{}`. Also, I noticed in your post that you omit #5, assigning them individually would allow for this as well.
Russ Bradberry
For #5 I just put an empty string. Why do you think it is less readable? Because then the explicit correlation of number with code is not present?
Emtucifor
that is correct
Russ Bradberry
A: 

Here are the results of my own speed testing (finally).

In my tests I used the following code. I used 1 million iterations to keep wait times down. -1 to 13 is to give some out of bounds work. When array setup is required, it was included in the total time, but only done once. Each function got its own calling procedure with the name (X) hardcoded:

Dim str As String = ""
For i As Integer = 0 To 1000000
   For j As Integer = -1 To 13
      str = ComputeLabel_X(j, False, False)
      str = ComputeLabel_X(j, True, False)
      str = ComputeLabel_X(j, False, True)
   Next
Next

I also got different results when running the code with F5 (break into code on error) and Ctrl-F5 (run outside of debugger). I guess the second one has more validity for the SSRS code environment as nothing would be attaching to the process to debug it.

All results are compile without debug (Ctrl-F5).

  • Russ: 613 ms (bounds checking added)
  • Erik: 614 ms (same as Russ but arrays populated as array literal {} instead of individual statements)
  • Erik2: 526 ms (0 to 12, with an extra 0 to 2, no bounds checking)
  • Steve: 660 ms
  • Steve2: 873 ms (remove ElseIf for individual If statements)
  • New: 2977 ms (The first idea from my previous answer)
  • Initial Post: 3915 ms * - revised to be correct (was 10 times too small)
  • Original: 3947 ms (the version you've never seen, the one I optimized to post here)
  • Choose: 11068 ms
  • BigArray: 12565 ms (calculating the index into a big array with math)

Even though execution times could fluctuate by as much as 100 ms for the higher values, the rankings tended to stay consistent, except for the Russ and Erik versions which kept swapping.

Takeaways:

  • Building the array once is insignificant. Doing it as individual statements or as an array literal {} is identical.

  • It cost 20% more to do bounds checking for the array method.

  • Expanding the entire thing intuitively seems like it should be fastest, but it isn't. I don't know why. Perhaps it has something to do with processor cache line size and preloading tradeoffs, or something like that.

  • The only real changes I made from the original function to the function I posted in my question were: 1) Return from each case statement instead of assign the string to a variable and return that at the end (plus removing the variable), 2) swap the independent If Flag statements order and change the second If to an ElseIf. The 1% improvement was insignificant

  • It seems like I should be able to generalize something from the fact that my version listed as "New" (the first query in the other answer I posted) did so badly. Is it the longer strings? Is it that Returning is equally fast no matter where in the procedure it's done, but falling out of the Case statement to execute more instructions is slow?

  • Russ's array version is fastest. An array lookup is faster than a case statement with string concatenation.

  • At this point I am not sure how to explain why the New version is faster.

  • The Choose function is super, super slow.

Because of these tests I have to award the answer for this question to nobugz who claimed that I couldn't optimize the given code. So far, he's been right!

Update: I am really sorry about the mistake I made that left off a zero on the number of iterations testing the Initial Post version. Things have been corrected now.

Addendum: The (corrected) testing code:

Module Module1

   Dim _Labels(12) As String
   Dim _ConditionalLabels(12) As String
   Dim _HeldLabels(12) As String

   Dim Labels() As String
   Dim ConditionalLabels() As String
   Dim HeldLabels() As String

   Dim OrderLabelsBigArray(38) As String

   Sub Main()
      Dim sw As New Stopwatch()
      sw.Start()
      ComputeLabelsFirstPosted()
      sw.Stop()
      Console.WriteLine("FirstPosted " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsRuss()
      sw.Stop()
      Console.WriteLine("Russ " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsErik()
      sw.Stop()
      Console.WriteLine("Erik " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsErik2()
      sw.Stop()
      Console.WriteLine("Erik2 " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsBigArray()
      sw.Stop()
      Console.WriteLine("BigArray " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsSteve()
      sw.Stop()
      Console.WriteLine("Steve " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsSteve2()
      sw.Stop()
      Console.WriteLine("Steve2 " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsNew()
      sw.Stop()
      Console.WriteLine("New " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsChoose()
      sw.Stop()
      Console.WriteLine("Choose " & sw.ElapsedMilliseconds & " ms")

      sw.Reset()
      sw.Start()
      ComputeLabelsOriginal()
      sw.Stop()
      Console.WriteLine("Original " & sw.ElapsedMilliseconds & " ms")

      Console.Read()
   End Sub

   Public Sub ComputeLabelsFirstPosted()
      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelFirstPosted(j, False, False)
            str = ComputeLabelFirstPosted(j, True, False)
            str = ComputeLabelFirstPosted(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelFirstPosted(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
      Dim Prefix As String = ""

      If IsConditional Then
         Prefix = "Conditional "
      ElseIf IsHeld Then
         Prefix = "Held "
      End If

      Select Case Action
         Case 0
            Return ""
         Case 1
            Return Prefix & "Cancelled"
         Case 2
            Return Prefix & "Discontinued"
         Case 3
            Return Prefix & "Suspended"
         Case 4
            Return Prefix & "Unsuspended"
         Case 6
            Return Prefix & "Collected"
         Case 7
            Return Prefix & "Released from Hold"
         Case 8
            Return Prefix & "Modified"
         Case 9
            Return Prefix & "Discontinued for the Future"
         Case 10
            Return Prefix & "Verified"
         Case 11
            Return Prefix & "Modified And Verified"
         Case 12
            Return "Hold " & Prefix & "Cancelled"
         Case Else
            Return ""
      End Select
   End Function

   Sub ComputeLabelsRuss()
      _Labels(0) = ""
      _Labels(1) = "Cancelled"
      _Labels(2) = "Discontinued"
      _Labels(3) = "Suspended"
      _Labels(4) = "Unsuspended"
      _Labels(6) = "Collected"
      _Labels(7) = "Released from Hold"
      _Labels(8) = "Modified"
      _Labels(9) = "Discontinued for the Future"
      _Labels(10) = "Verified"
      _Labels(11) = "Modified And Verified"
      _Labels(12) = "Hold Cancelled"

      _ConditionalLabels(0) = ""
      _ConditionalLabels(1) = "Conditional Cancelled"
      _ConditionalLabels(2) = "Conditional Discontinued"
      _ConditionalLabels(3) = "Conditional Suspended"
      _ConditionalLabels(4) = "Conditional Unsuspended"
      _ConditionalLabels(6) = "Conditional Collected"
      _ConditionalLabels(7) = "Conditional Released from Hold"
      _ConditionalLabels(8) = "Conditional Modified"
      _ConditionalLabels(9) = "Conditional Discontinued for the Future"
      _ConditionalLabels(10) = "Conditional Verified"
      _ConditionalLabels(11) = "Conditional Modified And Verified"
      _ConditionalLabels(12) = "Hold Conditional Cancelled"

      _HeldLabels(0) = ""
      _HeldLabels(1) = "Held Cancelled"
      _HeldLabels(2) = "Held Discontinued"
      _HeldLabels(3) = "Held Suspended"
      _HeldLabels(4) = "Held Unsuspended"
      _HeldLabels(6) = "Held Collected"
      _HeldLabels(7) = "Held Released from Hold"
      _HeldLabels(8) = "Held Modified"
      _HeldLabels(9) = "Held Discontinued for the Future"
      _HeldLabels(10) = "Held Verified"
      _HeldLabels(11) = "Held Modified And Verified"
      _HeldLabels(12) = "Hold Held Cancelled"

      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelRuss(j, False, False)
            str = ComputeLabelRuss(j, True, False)
            str = ComputeLabelRuss(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelRuss(ByVal Action As Integer, ByVal Held As Boolean, ByVal Conditional As Boolean) As String
      If Action < 0 OrElse Action > 12 Then Return ""
      If Conditional Then Return _ConditionalLabels(Action)
      If Held Then Return _HeldLabels(Action)
      Return _Labels(Action)
   End Function

   Public Sub ComputeLabelsNew()
      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelNew(j, False, False)
            str = ComputeLabelNew(j, True, False)
            str = ComputeLabelNew(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelNew(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
      Dim Status As String = ""

      Select Case Action
         Case 0
            Return ""
         Case 1
            Status = "Cancelled"
         Case 2
            Status = "Discontinued"
         Case 3
            Status = "Suspended"
         Case 4
            Status = "Unsuspended"
         Case 6
            Status = "Collected"
         Case 7
            Status = "Released from Hold"
         Case 8
            Status = "Modified"
         Case 9
            Status = "Discontinued for the Future"
         Case 10
            Status = "Verified"
         Case 11
            Status = "Modified And Verified"
         Case 12
            If IsConditional Then Return "Hold Conditional Cancelled"
            If IsHeld Then Return "Hold Held Cancelled"
            Return "Hold Cancelled"
         Case Else
            Return ""
      End Select
      If IsConditional Then Return "Conditional " & Status
      If IsHeld Then Return "Held " & Status
      Return Status
   End Function

   Sub ComputeLabelsErik()
      Labels = New String() {"", "Cancelled", "Discontinued", "Suspended", "Unsuspended", "", "Collected", "Released from Hold", "Modified", "Discontinued for the Future", "Verified", "Modified And Verified", "Hold Cancelled"}
      ConditionalLabels = New String() {"", "Conditional Cancelled", "Conditional Discontinued", "Conditional Suspended", "Conditional Unsuspended", "Conditional ", "Conditional Collected", "Conditional Released from Hold", "Conditional Modified", "Conditional Discontinued for the Future", "Conditional Verified", "Conditional Modified And Verified", "Hold  Cancelled"}
      HeldLabels = New String() {"", "Held Cancelled", "Held Discontinued", "Held Suspended", "Held Unsuspended", "Held ", "Held Collected", "Held Released from Hold", "Held Modified", "Held Discontinued for the Future", "Held Verified", "Held Modified And Verified", "Hold  Cancelled"}

      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelErik(j, False, False)
            str = ComputeLabelErik(j, True, False)
            str = ComputeLabelErik(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelErik(ByVal Action As Integer, ByVal Held As Boolean, ByVal Conditional As Boolean) As String
      If Action < 0 OrElse Action > 12 Then Return ""
      If Conditional Then Return ConditionalLabels(Action)
      If Held Then Return HeldLabels(Action)
      Return Labels(Action)
   End Function

   Sub ComputeLabelsErik2()
      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = 0 To 12
            str = ComputeLabelErik2(j, False, False)
            str = ComputeLabelErik2(j, True, False)
            str = ComputeLabelErik2(j, False, True)
         Next
         For j As Integer = 1 To 2
            str = ComputeLabelErik2(j, False, False)
            str = ComputeLabelErik2(j, True, False)
            str = ComputeLabelErik2(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelErik2(ByVal Action As Integer, ByVal Held As Boolean, ByVal Conditional As Boolean) As String
      If Conditional Then Return ConditionalLabels(Action)
      If Held Then Return HeldLabels(Action)
      Return Labels(Action)
   End Function

   Public Sub ComputeLabelsOriginal()
      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelOriginal(j, False, False)
            str = ComputeLabelOriginal(j, True, False)
            str = ComputeLabelOriginal(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelOriginal(ByVal Action As Integer, ByVal bIsHeld As Boolean, _
   ByVal bIsConditional As Boolean) As String

      Dim strReprintLabel As String = ""
      Dim strOrderActionPrefix As String = ""

      If (bIsHeld) Then
         strOrderActionPrefix = "Held "
      End If

      If (bIsConditional) Then
         strOrderActionPrefix = "Conditional "
      End If

      Select Case Action
         Case 0   ' Normal Order
            strReprintLabel = ""
         Case 1
            strReprintLabel = strOrderActionPrefix & "Order Cancelled"
         Case 2
            strReprintLabel = strOrderActionPrefix & "Order Discontinued"
         Case 3
            strReprintLabel = strOrderActionPrefix & "Order Suspended"
         Case 4
            strReprintLabel = strOrderActionPrefix & "Order Unsuspended"
         Case 6
            strReprintLabel = strOrderActionPrefix & "Order Collected"
         Case 7
            strReprintLabel = strOrderActionPrefix & "Order Released from Hold"
         Case 8
            strReprintLabel = strOrderActionPrefix & "Order Modified"
         Case 9
            strReprintLabel = strOrderActionPrefix & "Order Discontinued for the Future"
         Case 10
            strReprintLabel = strOrderActionPrefix & "Order Verified"
         Case 11
            strReprintLabel = strOrderActionPrefix & "Order Modified And Verified"
         Case 12
            strReprintLabel = "Hold " & strOrderActionPrefix & "Order Cancelled"
         Case Else
            strReprintLabel = ""
      End Select

      Return strReprintLabel
   End Function

   Sub ComputeLabelsSteve2()
      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelSteve2(j, False, False)
            str = ComputeLabelSteve2(j, True, False)
            str = ComputeLabelSteve2(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelSteve2(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
      Select Case Action
         Case 0
            Return ""
         Case 1
            If IsConditional Then Return "Conditional Cancelled"
            If IsHeld Then Return "Held Cancelled"
            Return "Cancelled"
         Case 2
            If IsConditional Then Return "Conditional Discontinued"
            If IsHeld Then Return "Held Discontinued"
            Return "Discontinued"
         Case 3
            If IsConditional Then Return "Conditional Suspended"
            If IsHeld Then Return "Held Suspended"
            Return "Suspended"
         Case 4
            If IsConditional Then Return "Conditional Unsuspended"
            If IsHeld Then Return "Held Unsuspended"
            Return "Unsuspended"
         Case 6
            If IsConditional Then Return "Conditional Collected"
            If IsHeld Then Return "Held Collected"
            Return "Collected"
         Case 7
            If IsConditional Then Return "Conditional Released from Hold"
            If IsHeld Then Return "Held Released from Hold"
            Return "Released from Hold"
         Case 8
            If IsConditional Then Return "Conditional Modified"
            If IsHeld Then Return "Held Modified"
            Return "Modified"
         Case 9
            If IsConditional Then Return "Conditional Discontinued for the Future"
            If IsHeld Then Return "Held Discontinued for the Future"
            Return "Discontinued for the Future"
         Case 10
            If IsConditional Then Return "Conditional Verified"
            If IsHeld Then Return "Held Verified"
            Return "Verified"
         Case 11
            If IsConditional Then Return "Conditional Modified And Verified"
            If IsHeld Then Return "Held Modified And Verified"
            Return "Modified And Verified"
         Case 12
            If IsConditional Then Return "Hold Conditional Cancelled"
            If IsHeld Then Return "Hold Held Cancelled"
            Return "Hold Cancelled"
         Case Else
            Return ""
      End Select
   End Function

   Sub ComputeLabelsSteve()
      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelSteve(j, False, False)
            str = ComputeLabelSteve(j, True, False)
            str = ComputeLabelSteve(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelSteve(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
      Select Case Action
         Case 0
            Return ""
         Case 1
            If IsConditional Then
               Return "Conditional Cancelled"
            ElseIf IsHeld Then
               Return "Held Cancelled"
            Else
               Return "Cancelled"
            End If
         Case 2
            If IsConditional Then
               Return "Conditional Discontinued"
            ElseIf IsHeld Then
               Return "Held Discontinued"
            Else
               Return "Discontinued"
            End If
         Case 3
            If IsConditional Then
               Return "Conditional Suspended"
            ElseIf IsHeld Then
               Return "Held Suspended"
            Else
               Return "Suspended"
            End If
         Case 4
            If IsConditional Then
               Return "Conditional Unsuspended"
            ElseIf IsHeld Then
               Return "Held Unsuspended"
            Else
               Return "Unsuspended"
            End If
         Case 6
            If IsConditional Then
               Return "Conditional Collected"
            ElseIf IsHeld Then
               Return "Held Collected"
            Else
               Return "Collected"
            End If
         Case 7
            If IsConditional Then
               Return "Conditional Released from Hold"
            ElseIf IsHeld Then
               Return "Held Released from Hold"
            Else
               Return "Released from Hold"
            End If
         Case 8
            If IsConditional Then
               Return "Conditional Modified"
            ElseIf IsHeld Then
               Return "Held Modified"
            Else
               Return "Modified"
            End If
         Case 9
            If IsConditional Then
               Return "Conditional Discontinued for the Future"
            ElseIf IsHeld Then
               Return "Held Discontinued for the Future"
            Else
               Return "Discontinued for the Future"
            End If
         Case 10
            If IsConditional Then
               Return "Conditional Verified"
            ElseIf IsHeld Then
               Return "Held Verified"
            Else
               Return "Verified"
            End If
         Case 11
            If IsConditional Then
               Return "Conditional Modified And Verified"
            ElseIf IsHeld Then
               Return "Held Modified And Verified"
            Else
               Return "Modified And Verified"
            End If
         Case 12
            If IsConditional Then
               Return "Hold Conditional Cancelled"
            ElseIf IsHeld Then
               Return "Hold Held Cancelled"
            Else
               Return "Hold Cancelled"
            End If
         Case Else
            Return ""
      End Select
   End Function

   Sub ComputeLabelsChoose()
      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelChoose(j, False, False)
            str = ComputeLabelChoose(j, True, False)
            str = ComputeLabelChoose(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelChoose(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
      Dim Status As String = ""

      Select Case Action
         Case 0, 5
            Return ""
         Case 1 To 11
            Status = Choose(Action, "Cancelled", "Discontinued", "Suspended", _
            "Unsuspended", "Collected", "Released from Hold", "Modified", _
            "Discontinued for the Future", "Verified", "Modified And Verified")
         Case 12
            If IsConditional Then
               Return "Hold Conditional Cancelled"
            ElseIf IsHeld Then
               Return "Hold Held Cancelled"
            Else
               Return "Hold Cancelled"
            End If
         Case Else
            Return ""
      End Select
      If IsConditional Then Return "Conditional " & Status
      If IsHeld Then Return "Held " & Status
      Return Status
   End Function

   Sub ComputeLabelsBigArray()
      OrderLabelsBigArray = New String() {"", "Cancelled", "Discontinued", "Suspended", "Unsuspended", "", "Collected", "Released from Hold", "Modified", "Discontinued for the Future", "Verified", "Modified And Verified", "Hold Cancelled", _
      "", "Conditional Cancelled", "Conditional Discontinued", "Conditional Suspended", "Conditional Unsuspended", "Conditional ", "Conditional Collected", "Conditional Released from Hold", "Conditional Modified", "Conditional Discontinued for the Future", "Conditional Verified", "Conditional Modified And Verified", "Hold  Cancelled", _
      "", "Held Cancelled", "Held Discontinued", "Held Suspended", "Held Unsuspended", "Held ", "Held Collected", "Held Released from Hold", "Held Modified", "Held Discontinued for the Future", "Held Verified", "Held Modified And Verified", "Hold  Cancelled"}

      Dim str As String = ""
      For i As Integer = 0 To 1000000
         For j As Integer = -1 To 13
            str = ComputeLabelChoose(j, False, False)
            str = ComputeLabelChoose(j, True, False)
            str = ComputeLabelChoose(j, False, True)
         Next
      Next
   End Sub

   Public Function ComputeLabelBigArray(ByVal Action As Integer, ByVal IsHeld As Boolean, ByVal IsConditional As Boolean) As String
      If Action < 0 OrElse Action >= 13 Then Return ""
      Return OrderLabelsBigArray(Action - IsConditional * 13 - (IsHeld AndAlso Not IsConditional) * 26)
   End Function
End Module

Now if I made another mistake, someone can help find it.

Emtucifor
Those results don't make sense. I mean, it's not consistent at all with my tests. Something must be wrong. Did you run every function inside one big benchmark or did you change the code and recompile for every attempt? And if you did it as one long benchmark, did you reset the stopwatch between each test? I mean... I'm befuddled right now.
Steve Wortham
OK, lol... perhaps I'm obsessed again but I just wrote another benchmark from scratch to mimic yours and updated my answer with it. I posted the whole thing this time. If you run it I wonder if you'll get the same results as I do.
Steve Wortham
You're absolutely right. Sigh beyond all sighs. You see, I examined my code and found I was missing nothing. That is, I was missing a small little zero at the end of my loop so that the Initial Post only ran 100,000 times instead of 1,000,000. So all the numbers are right, EXCEPT for the first one, which should be ten times larger. My huge apologies for this.
Emtucifor
Lo and behold, I also made another error, in assuming something was equivalent: `If A Then Return 'A' / If B Then Return 'B'` and `If A Then / Return 'A' / ElseIf B Then / Return 'B' / End If`. These are not equivalent, the second way is faster. I changed your suggested code in my test. Another lesson learned. Updating numbers again...
Emtucifor