In response to part 4, William Astle wrote a very nice expansion to my musings about INKEY and GOTO versus GOSUB. If you have been following my ramblings, I highly recommend you check out his posting. Unlike me, he actually understands what is going on behind the scenes:
One of the things he pointed out, then explained further in a comment after I didn’t understand, was how the GOSUB processing works. After the GOSUB keyword is found, BASIC acquires the line number and then scans to the end of the line or the next colon. That is where RETURN will RETURN to. This sounds as one might expect, but there was a bit of weirdness I didn’t “get” at first.
William demonstrated that anything after the line number is ignored, thus:
GOSUB 1000 I CAN TYPE STUFF HERE WITHOUT ERROR
…is valid. This surprised me. If you do this:
GOSUB 1000 I CAN TYPE STUFF HERE WITHOUT ERROR:PRINT "BACK FROM GOSUB"
…when the RETURN returns, you will see the “BACK FROM GOSUB” message printed as expected. Anything between the line number and the colon (or start of next line, whichever is found first) is ignored. William explains what is going on in his article.
This, of course, made me do some more stupid testing. First, I modified my benchmark program to run multiple tests and then average out the results. It looks like this:
0 REM BENCH.BAS 5 DIM TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 REM 40 REM PUT CODE TO BENCHMARK 50 REM HERE. 60 REM 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END
Then I reran my GOSUB test:
0 REM GOSUB3.BAS 5 DIM TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 GOSUB100 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END 100 RETURN
When I run this, it prints the time taken for each run, and then the average:
Now for the stupid test, I added some junk after “GOSUB 100” and filled it up to the end of the line.
Side Note: I am loading BASIC programs in ASCII, so the program lines load in as if they were being typed in. Thus, it counts the characters “100 GOSUB ” as part of it. But, as soon as you press ENTER, that line is tokenized and GOSUB becomes a 1-byte token (is it 1-byte?). Then you can EDIT the line and Xtend it and type in a few more characters. So what I show here isn’t the max line size, but it is the max line size I could load in from an ASCII BASIC file. But I digress.
My line looks like this:
30 GOSUB100 ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRST*
Now when I run this, the extra “scan to the end” time causes the benchmark to show 1507!
But who would do that? If anything, you would have a colon and real stuff after the GOSUB. So I tried this by changing the space after “GOSUB 100” to a colon and “REM”:
Now that’s a completely legitimate line. (Pretend the ABC/123 gibberish is a really long comment.)
This benchmark shows 1508, so no real difference. When GOSUB is encountered, BASIC has to scan to the end of line or a colon, whichever comes first, so it should find the colon instantly, BUT, after the RETURN it still has to scan through that REM to find the next line. Thus, it’s the same amount of scanning.
This is a meaningless test.
With real code, you might be doing something like this:
30 GOSUB 100:PRINT "BACK FROM ROUTINE"
Or you could have written it out as two lines:
30 GOSUB 100 40 PRINT "BACK FROM ROUTINE"
I thought the first one should be faster, since it has one less line.
And combining lines is good.
Well, in my silly example #2 above, what if I moved the REM to the next line, like this:
30 GOSUB100 40 REMABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRST*
That’s basically the same, just with an extra line number.
When I run this, I get a benchmark value of … 860!
Look how much faster it is by moving code to a separate line! I guess we should use separate lines after all, then…
What’s going on here? The key seems to be the “REM” keyword. When BASIC encounters a REM, it can just skip to the next line. That makes it faster. But, it seems to be doing something different when a REM is in the middle of a line.
It appears it is faster to NOT put REMarks after a GOSUB.
30 GOSUB 100:REM MOVE PLAYER UP
…shows 266. This is slower than…
30 GOSUB 100 40 REM MOVE PLAYER UP
…which shows 220. And I’ve certainly seen programmers make use of the apostrophe REMark shortcut.
The apostrophe represents “:REM” (colon REM) so these two are the same:
30 GOSUB 100:REM MOVE PLAYER UP 30 GOSUB 100' MOVE PLAYER UP
Thus, using the one character apostrophe may look like it saves code space versus “:REM” but it does not. It does save printer paper, though :)
But I digress…
It looks like I’m going to need another test. In the meantime, don’t put things after a GOSUB om the same line. It appears to be faster to put them on the next line:
30 REM MOVE PLAYER UP 40 GOSUB100
That is 219.
30 GOSUB100:REM MOVE PLAYER UP
That is 266!
30 GOSUB100 40 REM MOVE PLAYER UP
That is backwards. But it produces 220, so it doesn’t penalize you for being backwards.
Oh, and as Steve Bjork pointed out in the Facebook group, a faster solution is not to use REMs at all. I think I need smarter examples. There are too many real programmers watching. For you folks:
0 REM THIS TAKES 371 5 DIM Z,TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 GOSUB100:Z=Z+1 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END 100 RETURN
0 REM THIS TAKES 357 5 DIM Z,TE,TM,B,A,TT 10 FORA=0TO4:TIMER=0:TM=TIMER 20 FORB=0TO1000 30 GOSUB100 40 Z=Z+1 70 NEXT:TE=TIMER-TM 80 TT=TT+TE:PRINTA,TE 90 NEXT:PRINTTT/A:END 100 RETURN
A few additional REMarks…
Above, I mentioned that the apostrophe represented “:REM”. Thus, doing something like this:
100 'MOVE UP
Is slower than doing:
100 REM MOVE UP
It may look smaller, but the first example is like scanning “:REM MOVE UP” and the second is just “REM MOVE UP” so it has less work to do.
And yes, I tested it inside the benchmark program:
…is also 90.
I guess it’s just treating the apostrophe as “:REM” internally, or maybe it’s a 2-byte token for “:REM” versus a different 1-byte token just for “REM” or something. Dunno.
Until next time…