For ... Next
|
- Next or Next i ... what's faster?
- What loops quicker: a 2-byte Integer or a 4-byte Long?
- How does compiler option Integer Overflow Check affect performance?
|
|
Code |
It's tricky to set up a speed test for ForNext loops:
- ForNext loops are extremely fast and any code inside the loop is likely to take much more time than the looping itself und thus covers the differences we are looking for. Omitting any in-loop code, however, is no good solution either: if Integer Overflow Check is turned ON we do get a stable ratio between the number of loops and the time it takes to complete them, but what we measuring there is the Integer Overflow Check, not the increment of the loop counter itself. If Integer Overflow Check is turned OFF, the compiler appears to drop the whole loop if it sees no in-loop code, thus optimizing our test into nirvana.
- Another problem arises from codespit-alignment: since we are looking for very short time differences access time to the executed code in memory becomes significant, and access time also depends on the code's alignment. Unfortunately, you cannot directly control how the compiler aligns the code -- it's a matter of good or bad luck. Inserting a dummy line somewhere before the tested part of the code can change the alignment in significant but unpredictable ways. So, it's absolutely possible that an additional line of code can speed up a procedure!
- After hundreds of tests i finally found a piece of in-loop code that's (a) very fast, and (b) is not optimized by the compiler irrespective of the compiler options setup:
If fFoo Then lFoo = lFoo + 1
where fFoo and lFoo are public variables.
The timed code now looks like this (there are two nested loops to allow the Integer test to run a million times without overflowing):
|
Public fFoo As Boolean
Public lFoo As Long
Public Sub [IntNext|IntNextI|LongNext|LongNextI]()
Dim i As [Integer|Long], j As [Integer|Long]
For i = 1 To 1000
For j = 1 To 1000
If fFoo Then lFoo = lFoo + 1
Next [j]
Next [i]
End Sub
|
Calls |
1 | Integer Overflow Check OFF, fFoo = False
|
2 | Integer Overflow Check OFF, fFoo = True
|
3 | Integer Overflow Check ON, fFoo = False
|
4 | Integer Overflow Check ON, fFoo = True
|
|
VB5 Charts |
Code |
Integer Next |
Integer Next i |
Long Next |
Long Next i |
|
Call 1 |
2 | 1.00 | 14,444µs |
3 | 1.00 | 14,429µs |
1 | 1.00 | 14,408µs |
4 | 1.00 | 14,468µs |
|
Call 2 |
4 | 1.00 | 11,586µs |
3 | 1.00 | 11,557µs |
2 | 1.00 | 11,551µs |
1 | 1.00 | 11,542µs |
|
Call 3 |
4 | 2.50 | 28,861µs |
3 | 2.50 | 28,839µs |
1 | 1.00 | 11,558µs |
2 | 1.00 | 11,564µs |
|
Call 4 |
3 | 2.00 | 25,972µs |
4 | 2.00 | 25,981µs |
2 | 1.00 | 13,024µs |
1 | 1.00 | 12,969µs |
|
|
VB6 Charts |
Code |
Integer Next |
Integer Next i |
Long Next |
Long Next i |
|
Call 1 |
2 | 1.00 | 14,424µs |
4 | 1.00 | 14,464µs |
1 | 1.00 | 14,417µs |
3 | 1.00 | 14,443µs |
|
Call 2 |
4 | 1.00 | 11,583µs |
2 | 1.00 | 11,550µs |
1 | 1.00 | 11,542µs |
3 | 1.00 | 11,565µs |
|
Call 3 |
3 | 2.50 | 28,834µs |
4 | 2.50 | 28,870µs |
1 | 1.00 | 11,543µs |
2 | 1.00 | 11,553µs |
|
Call 4 |
4 | 2.00 | 25,926µs |
3 | 2.00 | 25,930µs |
1 | 1.00 | 12,989µs |
2 | 1.00 | 13,029µs |
|
|
Conclusions |
- Despite all rumors: Next or Next i does NOT matter at all!! The minimal differences you see in the timings above are totally insignificant: you could produce any possible ranking order by running the same test over and over again long enough.
- What does matter though is whether you set compiler option Integer Overflow Check OFF or ON:
(a) when checking is OFF, Integer or Long does not affect performance. Interestingly, when the fFoo flag is True (Call 2) the code runs faster although more work is done. The only explanation i have for this is that True-conditions are processed faster than False-conditions. Or, rather, it sometimes looks that way because in a false condition, the instruction pointer has to bounce to another location in memory. And memory jumps are invariably slow.
(b) when checking is ON, Integer loops take about two times longer than with checking OFF, which is no surprise. They also take 2 (resp. 2.5) times longer than Long loops, which can only mean that the overflow checking for Integer variables is a more complex task than for Long variables. Things become weird with Long and fFoo = False (Call 3): they process faster than with checking OFF (Call 1), which i cannot explain. When the fFoo flag is True (Call 4), they perform a bit slower than with checking OFF (but the slow-down is clearly smaller than with Integer loops).
- Things are the same for VB5 and VB6.
The bottom line is:
- Do or do not repeat loop counters in the Next statement -- it's just a matter of taste and does not affect speed (and it would be a miracle if it did: both alternatives generate identical code in the executable).
- To be on the safe side (irrespective of compiler settings) always use Long variables as loop counters. Remember, however, that ForNext loops are very fast anyway: the possible speed gains are minimal in absolute terms and become practically irrelevant whenever the code executed within the loop is heavier than just a simple line, or when the loop is excuted only a few thousand times.
- A by-product of the tests above: True-conditions perform faster. So, if you can make assumptions about your conditions, set up the code so that the test returns True.
|
Got comments? |
 |
|