See also: part 1
There is a time and ELSE for everything
After I dove into ELSE efficiency, and then dove into it a bit deeper, I realized one of the major things that slows down BASIC is having to scan to the end of the line in order to find the next line.
In part 7 of my Optimizing Color BASIC series, I noticed that a GOTO or GOSUB was much slower if there were things on the line after it:
10 GOTO 100:REM THIS GOES TO THE INPUT ROUTINE
When BASIC is searching for a line number, each line entry has a size which lets it skip ahead to the next line if the line number did not match. But, once BASIC is processing a line, it does not track that. If the parser gets one token in to a line and it has to GOTO somewhere else, it has to scan forward until it finds the end of the line, then it can start scanning line numbers and skipping lines again.
Thus, one should not put comments on the ends of lines. They have to be parsed through. It is much faster to put comments on their own lines, though that takes up a more memory since each new line number takes 5 bytes.
This is one of the reasons this…
30 IF Z=1 THEN 100 ELSE IF Z=2 THEN 200 ELSE IF Z=3 THEN 300 ELSE IF Z=4 THEN 400
…can be slower than…
30 IF Z=1 THEN 100 31 IF Z=2 THEN 200 32 IF Z=3 THEN 300 33 IF Z=4 THEN 400
In the first example, if Z=1, BASIC still has to scan through all the remaining bytes of the line until it finds the end. In the second example, BASIC gets the line number then is already at the end and can immediately start scanning.
Thus, to add to my previous ELSE discussion, we should stop doing this:
30 IF X=1 THEN X=X+1:GOTO 70
If X is NOT 1, BASIC still has to scan through the rest of the line before it can check whatever happens next. Instead, it ican be much faster to do this:
30 IF X=1 THEN 100 ... 100 X=X+1:GOTO xxx
There is a caveat to this. Things in BASIC are quite predictable. This change makes it faster to get to line 100 to do the actual work (X=X+1). BUT, from line 100 it could be much slower to get back to the top if there were a bunch of lines before the target line. If it’s a GOTO near the top of the program, it’s fast. If it’s a loop around 500, and you try to GOTO 500, that could be much slower if it had to scan through all the lines from 10 to 499 to get there.
This is when organizing code locations comes in very important, but that is a discussion for another time.
But other than situations like that, this is faster way:
10 REM MAIN LOOP 20 REM GET Z OR WHATEVER 30 IF Z=1 THEN 100 31 IF Z=2 THEN 200 32 IF Z=3 THEN 300 33 IF Z=4 THEN 400 40 REM NO KEY, DO IDLE LOOP STUFF 50 GOTO 10 100 X=X+1:GOTO 10 200 X=X-1:GOTO 10 300 Y=Y+1:GOTO 10 400 Y=Y-1:GOTO 10
If Z=4, the code will get there much faster since there is less for it to scan through after each Z check.
The location of where the work is done (later in the program or back at the top) is the only thing that can make this slower (or faster). Once a program gets large enough, moving things around may help speed up certain things but will slow down other things. I guess, much like real estate, location matters.
I guess we can stop using ELSE, and stop doing work on IF lines. And that goes for things like this:
IF SC=1000 THEN LV=LV+1
At 1000 points, we get a new life! However, every time through that loop when SC is not 1000, BASIC has to skip over “THEN LV=LV+1”, wasting cycles. The savings would be huge over the life of that loop to just do:
IF SC=1000 THEN 1000
…and let 1000 handle LV=LV+1 and returning back with a GOTO. GOSUB might be better, but that might be slower for something like this since:
IF SC=1000 THEN GOSUB 1000
…has an extra token (and potentially spaces, if you use them for readability) versus:
IF SC=1000 THEN 1000
WHEN this code runs (SC=1000), it may be faster to GOSUB and RETURN than it would be to GOTO/GOTO. BUT, most of the time the main loop is running that code will not be called and parsing past the GOSUB will just be wasting time.
Until next time…