Comparing Forall To For Loops
Recently, I have heard some rumblings about Forall loops being slower than traditional For loops. So I decided to implement a test to verify one way or another. My findings indicate that there is no noticable performance difference between the two methods. To test the performance difference, I used two methods. One uses a standard array and the other uses the values from a Notes field. Some data setup is needed, so let's cover that first:
Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim item As NotesItem
Dim array(3000) As String
Dim i As Integer
Dim startTime As New NotesDateTime("")
Dim endTime As New NotesDateTime("")
Dim outerLoop As Integer
Dim results As String
Dim value As Variant
Dim lower As Integer
Dim upper As Integer
Set db = session.CurrentDatabase
Set doc = db.CreateDocument
Call doc.ReplaceItemValue("Test", "")
Set item = doc.GetFirstItem("Test")
For i = Lbound(array) To Ubound(array)
Print "Building data (" & Cstr(i) & ")...."
array(i) = Format$(i, "0000")
Call item.AppendToTextList(Format$(i, "0000"))
Next
A total of 3000 records are used. (I needed to keep things under the limit for a single multi-valued field in Notes). When running the agent, the Print statement is interesting. As you get closer to 3000, each iteration slows down. Either the AppendToTextList method starts at the front of the field to find the place to append (which would explain it slowing down) or building the document and array in memory takes up more resources which slows things down.
Next, I wanted to process the data. The first time through the array is processed. To make for a valid test (without introducing a lot of other items), all the items in the loop are processed and each value is just printed out. Since processing 3000 records doesn't take that much time, an outer loop is added to go through all 3000 records 5 times total. Here's the two different methods - the "for" on the left and the "forall" on the right.
results = "When Processing The Array:" & Chr$(10)
Call startTime.SetNow For outerLoop = 1 To 5 lower = Lbound(array) upper = Ubound(array) For i = lower To upper Print Cstr(outerLoop) & " " & array(i) Next Next Call endTime.SetNow results = results & "For loop started " & _ Format$(startTime.LSLocalTime, "hh:nn:ss") & _ " and ended " & _ Format$(endTime.LSLocalTime, "hh:nn:ss") & _ " for an elapsed time of " & _ Cstr(endTime.TimeDifference(startTime)) & _ " seconds." & Chr$(10) |
Call startTime.SetNow For outerLoop = 1 To 5 Forall arrayElements In array Print Cstr(outerLoop) & " " & arrayElements End Forall Next Call endTime.SetNow results = results & "Forall loop started " & _ Format$(startTime.LSLocalTime, "hh:nn:ss") & _ " and ended " & _ Format$(endTime.LSLocalTime, "hh:nn:ss") & _ " for an elapsed time of " & _ Cstr(endTime.TimeDifference(startTime)) & _ " seconds." & Chr$(10) & Chr$(10) |
Notice that the "for" loop version, the upper bound and lower bound are evaluated outside the loop. This was done just in case putting those statements on the "for" loop statement itself would slow things down. (Turns out, it didn't affect the performance). Besides that, the code should be similar, with no external factors affecting performance.
For the Notes item, the code is similar:
results = results & "When Processing The Notes Item:" & Chr$(10)
value = doc.GetItemValue("Test")
Call startTime.SetNow For outerLoop = 1 To 5 lower = Lbound(array) upper = Ubound(array) For i = lower To upper Print Cstr(outerLoop) & " " & value(i) Next Next Call endTime.SetNow results = results & "For loop started " & _ Format$(startTime.LSLocalTime, "hh:nn:ss") & _ " and ended " & _ Format$(endTime.LSLocalTime, "hh:nn:ss") & _ " for an elapsed time of " & _ Cstr(endTime.TimeDifference(startTime)) & _ " seconds." & Chr$(10) |
Call startTime.SetNow For outerLoop = 1 To 5 Forall valueElements In value Print Cstr(outerLoop) & " " & valueElements End Forall Next Call endTime.SetNow results = results & "Forall loop started " & _ Format$(startTime.LSLocalTime, "hh:nn:ss") & _ " and ended " & _ Format$(endTime.LSLocalTime, "hh:nn:ss") & _ " for an elapsed time of " & _ Cstr(endTime.TimeDifference(startTime)) & _ " seconds." & Chr$(10) & Chr$(10) |
At the end, we need to print out the results:
Msgbox results, 64, "Results"
End Sub
I ran through this test several times, with nothing else running on my machine. Here is a screen shot of a sample:
screen shot of typical results.
The results seem to point out that there isn't much, if any, of a difference between using a Forall loop and a For loop. If there is anyone out there who can show proof to the contrary, please post your findings in our comments area below.