API
@-Formulas
JavaScript
LotusScript
Reg Exp
Web Design
Notes Client
XPages
 
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.