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.