ReDim and Forms - Don't do it
September 9, 2010 12:00Recently, I was faced with a Visual Basic program that was running quite slowly that really needed to undergo some basic optimizations to achieve the speed desired. I began going through the code to identify the slow points, setting timers in different locations just to get an idea of where the code was getting bogged down. After a little bit of time spent doing so, I was faced with a method with five for loops, none of them nested, which were undergoing a total of only 10,000 iterations or so, but these for loops were dominating the execution time of the entire program. It seemed like a classic case of 95% of the time spent in just 5% of the code.
The average execution time for each time this method was called was approximately 1.5 seconds. This method alone was called four times on startup. So, the startup time seemed to be completely dependent on the execution time of this method alone. So what was causing the method to take so long? Redim and accessing forms objects.
Now, before you go and state that you already knew that performing these two operations were expensive operations, I'll bet you didn't know that they were THIS expensive. I'll use a little example to demonstrate. The following example serves no functional purpose other than to demonstrate exactly how time intensive these operations actually are.
This method sets the text in TextBox1 to numElements, sets the first 100 elements of the array to values equal to their index, then the fun begins. We enter a for loop, and on every iteration, the array is expanded by one element, and that element is given a value. Obviously a poor way to this, but let's take a look at how poor it actually is. The ouput of this program is:
Dim numElements As Integer = 100 Dim theArray(numElements) As Integer Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim watch As New Stopwatch watch.Start() TextBox1.Text = numElements For i As Integer = 0 To numElements theArray(i) = i Next For i As Integer = 1 To 75000 ReDim Preserve theArray(CInt(TextBox1.Text) + i) theArray(i) = i Next watch.Stop() Console.WriteLine("Method took " & watch.ElapsedMilliseconds & " ms") End Sub
16 seconds to run this short little method. Now, let's remove the call to the text box during each iteration of the for loop, changing the for loop to:
Method took 15780 ms
Removing the call to the form object takes off 2 seconds, or 12.6 % of the execution time. Now, let's fix the Redim problem by calling it once before the for loop is executed:
For i As Integer = 1 To 75000 ReDim Preserve theArray(CInt(numElements) + i) theArray(i) = i Next Method took 13786 ms
Yup. By making only a single call to ReDim rather than calling it during every single iteration of the for loop, the method drops to only 1 ms for its entire execution. Now, I'll bet those aren't the results you were expecting. Moral of the story? Avoid getting data directly from form objects when possible, and only in absolutely necessary conditions ReDim an array, and NEVER stick it in some kind of loop. Do it beforehand.
ReDim Preserve theArray(75000) For i As Integer = 1 To 75000 theArray(i) = i Next Method took 1 ms
Oh, and that method that I was talking about at the beginning? It now runs in less than 1 millisecond.