Let's take a moment to look at how our QB64 code translates into C-code.
First, let's start with a simple FOR...NEXT loop:
Now, we can't get any simpler than this, and even this simple little batch of code turns into the following:
S_1:;
fornext_value2= 0 ;
fornext_finalvalue2= 100 ;
fornext_step2= 1 ;
if (fornext_step2<0) fornext_step_negative2=1; else fornext_step_negative2=0;
if (new_error) goto fornext_error2;
goto fornext_entrylabel2;
while(1){
fornext_value2=fornext_step2+(*__SINGLE_I);
fornext_entrylabel2:
*__SINGLE_I=fornext_value2;
if (fornext_step_negative2){
if (fornext_value2<fornext_finalvalue2) break;
}else{
if (fornext_value2>fornext_finalvalue2) break;
}
fornext_error2:;
if(qbevent){evnt(1);if(r)goto S_1;}
fornext_continue_1:;
}
fornext_exit_1:;
As you can see, there's quite a bit of error checking and conditioning checking going on in the code. Each time the code runs, we have to check to see if the fornext_step_negative2 is negative or not, and then we check to see if our current value is greater (or lesser) than the final value.
Now, let's compare this code to how a DO...LOOP is translated. First, let's start with the QB64 code:
And the translation to this simple DO...LOOP code:
do{
*__SINGLE_I= 0 ;
if(!qbevent)break;evnt(1);}while(r);
S_2:;
do{
if(qbevent){evnt(2);if(r)goto S_2;}
do{
*__SINGLE_I=*__SINGLE_I+ 1 ;
if(!qbevent)break;evnt(3);}while(r);
S_4:;
dl_continue_1:;
}while((!(-(*__SINGLE_I> 100 )))&&(!new_error));
dl_exit_1:;
if(qbevent){evnt(4);if(r)goto S_4;}
As you can see, there's a lot fewer IF checks going in inside the DO..LOOP, as we don't need to check to see if we're counting up or down with the command. Those checks are dependent on the user to code what's proper for their needs.
**************************
And, if we turn the checking off, like in the following code, it's even easier to see exactly how large a difference there is in the two processes.
With checking off, the above code translates into a very simple little:
*__SINGLE_I= 0 ;
do{
*__SINGLE_I=*__SINGLE_I+ 1 ;
dl_continue_1:;
}while((!(-(*__SINGLE_I> 100 )))&&(!new_error));
dl_exit_1:;
And, if we do the same with the FOR..NEXT loop, what we see is the following:
Which translates to:
fornext_value2= 0 ;
fornext_finalvalue2= 100 ;
fornext_step2= 1 ;
if (fornext_step2<0) fornext_step_negative2=1; else fornext_step_negative2=0;
if (new_error) goto fornext_error2;
goto fornext_entrylabel2;
while(1){
fornext_value2=fornext_step2+(*__SINGLE_I);
fornext_entrylabel2:
*__SINGLE_I=fornext_value2;
if (fornext_step_negative2){
if (fornext_value2<fornext_finalvalue2) break;
}else{
if (fornext_value2>fornext_finalvalue2) break;
}
fornext_error2:;
fornext_continue_1:;
}
fornext_exit_1:;
Which, once we compare the two routines closely against each other, easily (in my opinion) showcases why the DO...LOOP structure is inherently faster than the FOR...NEXT translation.
************************
As to why some people are seeing faster times with the FOR..NEXT, over the DO...LOOP, I have no idea what's going on. Logic tells us that it's going to be faster to simply add a value to a number, than it is to add a value to a number and then check to see if it's counting up or down... DO simply has fewer steps to its process than FOR does.
Out of curiosity, could you guys who are seeing the faster FOR loops add the $CHECKING:OFF command to Bplus's code and then run it. I'm curious how much of a delay the internal error checking processes are making on the code.
Tr this, and see how it performs for you folks:
PRINT "Running 4 timed tests please wait..."
n = 1000000000
x = 0: i = 0
doloop:
i = i + 1
x = x + i
x = 0: i = 0
i = i + 1
x = x + i
x = 0
x = x + i
x = 0: i = 0
i = i + 1
x = x + i
PRINT n;
" times adding in Do... LOOP time is "; t5!
- t4!
PRINT n;
" times adding in FOR... NEXT time is "; t4!
- t3!
PRINT n;
" times adding in While... Wend time is "; t3!
- t2!
PRINT n;
" times adding in GOTO loop time is "; t2!
- t!