Basic Speedup Tip
Moderator: Moderators
Basic Speedup Tip
Basic Speedup Tip
When programming my 20 line basic game, I discovered something... I don't know if this is well known, but at least I didn't know about it. Here we go...
The variables first used in your program will be the fastest variables throughout the whole program. For example, if your program starts with this:
10 X=36879: F=12 : R=255 : B=45
Then X will be accessed faster than F. F will in turn be faster than R that will be faster than B. In the whole program. So to speedup a program, take a look at the most demanding part of your program to see which variables that are used the most. If you for example see that X, G and T are the ones you use the most, then just add something like this first in your program:
0 X=1:G=1:T=1
And it will speed up!!! For me it made quite a difference...
This must also mean that using as few variables as possible will be the best.
I have also noticed (before) that using variables is faster than using constants. For example:
10 A=2:B=12
20 FOR T=1TO500:Z=RND(1)*A/B:NEXT
is faster than
10 FOR T=1TO500:Z=RND(1)*2/12:NEXT
....as long as you don't use too many variables. So using a few variables (maybe re-use them for different things) and sometimes use variables instead of constants should be things to think about if you want to make a fast program.
Happy basic hacking!
/Anders Persson
http://listen.to/boray
When programming my 20 line basic game, I discovered something... I don't know if this is well known, but at least I didn't know about it. Here we go...
The variables first used in your program will be the fastest variables throughout the whole program. For example, if your program starts with this:
10 X=36879: F=12 : R=255 : B=45
Then X will be accessed faster than F. F will in turn be faster than R that will be faster than B. In the whole program. So to speedup a program, take a look at the most demanding part of your program to see which variables that are used the most. If you for example see that X, G and T are the ones you use the most, then just add something like this first in your program:
0 X=1:G=1:T=1
And it will speed up!!! For me it made quite a difference...
This must also mean that using as few variables as possible will be the best.
I have also noticed (before) that using variables is faster than using constants. For example:
10 A=2:B=12
20 FOR T=1TO500:Z=RND(1)*A/B:NEXT
is faster than
10 FOR T=1TO500:Z=RND(1)*2/12:NEXT
....as long as you don't use too many variables. So using a few variables (maybe re-use them for different things) and sometimes use variables instead of constants should be things to think about if you want to make a fast program.
Happy basic hacking!
/Anders Persson
http://listen.to/boray
Yep, using variables is faster than numeric constants on all 8-bit BASICs except the Sinclair Basic (found in ZX81 and ZX Spectrum) where the opposite holds true.
The same thing goes for subroutines and I believe DATA statements; put both as early as possible and in the first line of your program, GOTO down to the main program if it does a lot of subroutine calls.
I guess technically you can fiddle with the interrupt timers (IIRC 37879 is a good one) to let Basic work more without being interrupted, but you should put it back to original values when you require timing, output or otherwise.
I'm looking forward for the next compo, which will be about writing a 256 byte or less machine code routine for use in combination with a Basic program, where one can show great improvements over doing the same work in Basic only.
The same thing goes for subroutines and I believe DATA statements; put both as early as possible and in the first line of your program, GOTO down to the main program if it does a lot of subroutine calls.
I guess technically you can fiddle with the interrupt timers (IIRC 37879 is a good one) to let Basic work more without being interrupted, but you should put it back to original values when you require timing, output or otherwise.
I'm looking forward for the next compo, which will be about writing a 256 byte or less machine code routine for use in combination with a Basic program, where one can show great improvements over doing the same work in Basic only.
Anders Carlsson
Well, I've noticed in my amatuer efforts that GOTO and GOSUB are greatly affected by line order.
I suspect when GOTO is called the computer starts from that position if the destination line number is greater, and starts from the beginning of the program if the number is lower. The computer can only scan in one direction.
I put my main loop as early as possible in games. For example:
1 GOTO 50 (This sends us to the end of the program to set up the screen, variables, etc.)
2 PRINT A$ (this would be my main loop)
3 GOTO 2 (the program is faster because it doesn't have to scan through much to get back to the loop, 3 tokens as opposed to 9 not including the line headers)
50 A$= "Test"
Did that make sense? It is just my suspicion.
I suspect when GOTO is called the computer starts from that position if the destination line number is greater, and starts from the beginning of the program if the number is lower. The computer can only scan in one direction.
I put my main loop as early as possible in games. For example:
1 GOTO 50 (This sends us to the end of the program to set up the screen, variables, etc.)
2 PRINT A$ (this would be my main loop)
3 GOTO 2 (the program is faster because it doesn't have to scan through much to get back to the loop, 3 tokens as opposed to 9 not including the line headers)
50 A$= "Test"
Did that make sense? It is just my suspicion.
When performing GOTO or GOSUB the interpreter can only scan forward through lines as there are no reverse links in the BASIC line structure.
The actual length of the line doesn't matter, a long line will be skipped over as quickly as a short line.
If your main loop is quite long and there is a fair bit of code before it then it may be faster to do something like ..
In this case the loop will never terminate and the start of the loop is pulled off the stack by the NEXT command. This can be much quicker than using a GOTO.
With DATA statements the penalty is only for the first READ after RUN or the first READ after RESTORE, as the interpreter has to search for the first DATA statement from the start of the program.
Other speed ups include..
Don'tusespacesinalineastheytaketimetoskip. 8^)=
Put:as:many:commands:on:each:line:as:possible.
Re-use variables as often as possible.
Don't do repetetive calculations inside loops e.g. don't do ..
.. do ..
Cheers,
Lee.
The actual length of the line doesn't matter, a long line will be skipped over as quickly as a short line.
If your main loop is quite long and there is a fair bit of code before it then it may be faster to do something like ..
Code: Select all
.
. [lots of lines]
.
250 FOR A=1 TO -1
.
. [lots of lines]
.
340 A=A-1
350 NEXT A
With DATA statements the penalty is only for the first READ after RUN or the first READ after RESTORE, as the interpreter has to search for the first DATA statement from the start of the program.
Other speed ups include..
Don'tusespacesinalineastheytaketimetoskip. 8^)=
Put:as:many:commands:on:each:line:as:possible.
Re-use variables as often as possible.
Don't do repetetive calculations inside loops e.g. don't do ..
Code: Select all
100 FOR A=1 TO 100
110 B(A)=RND(0)*X/Y
120 NEXT
Code: Select all
100 SL=X/Y
110 FOR A=1 TO 100
115 B(A)=RND(0)*SL
120 NEXT
Lee.
Here's my favorite one. It's my personal secret.
When you have a solitary zero in a program.
A = 0
replace it with a period.
A = .
It take BASIC slightly longer to search for a zero than a period. It seems silly at first, but if you run a long loop, you can see the difference in time.
My programs are littered with periods.
When you have a solitary zero in a program.
A = 0
replace it with a period.
A = .
It take BASIC slightly longer to search for a zero than a period. It seems silly at first, but if you run a long loop, you can see the difference in time.
My programs are littered with periods.
Yes, the stack will be flooded, and you get ?OUT OF MEMORY, just like if you did a lot of GOSUB without RETURN.
Another tips in DATA statements is that if you're reading numerical values, you can omit value completely if you want a zero:
DATA 255,0,0,0,0,0,0,0,255,0,255,0,0,255,0,255
can be written: DATA 255,,,,,,,,255,,255,,,255,,255
I wonder if BASIC really starts scanning from current line number, or always from beginning of program for a GOTO or GOSUB?
By the way, other BASIC implementations have a RESTORE nn syntax, where nn is a line number. It is quite possible to accomplish on the VIC too by POKEing addresses 65/66 IIRC. I can't remember if you should give a line number or the actual address though, but either way it should be divided into low/high byte.
Another tips in DATA statements is that if you're reading numerical values, you can omit value completely if you want a zero:
DATA 255,0,0,0,0,0,0,0,255,0,255,0,0,255,0,255
can be written: DATA 255,,,,,,,,255,,255,,,255,,255
I wonder if BASIC really starts scanning from current line number, or always from beginning of program for a GOTO or GOSUB?
By the way, other BASIC implementations have a RESTORE nn syntax, where nn is a line number. It is quite possible to accomplish on the VIC too by POKEing addresses 65/66 IIRC. I can't remember if you should give a line number or the actual address though, but either way it should be divided into low/high byte.
Anders Carlsson
I think you are wrong there... I just tried this:carlsson wrote:Yes, the stack will be flooded, and you get ?OUT OF MEMORY, just like if you did a lot of GOSUB without RETURN.
10 FORT=1TO10:IFT=3THEN10
20 NEXT
Runs forever.... (it seems)
While this one:
10 GOSUB10
Generates an "OUT OF MEMORY" error message right away...
/Anders
You could make a dummy program to check. I am 95% sure that it starts from the line rather than the beginning if the number sought is higher.carlsson wrote:I wonder if BASIC really starts scanning from current line number, or always from beginning of program for a GOTO or GOSUB?
I also use the comma trick often, but with the contest I avoided doing so just as I avoided abbreviations. It makes it difficult to read the program.
When a GOTO or GOSUB is encountered BASIC does this..
For READ the scan is always initially from the program start.
Lee.
Code: Select all
Find the start of the next line
Compare current line number with GOTO(GOSUB) number
If current >= GOTO(GOSUB) number then
start from the program beginning
Else
start from the next line
End if
Lee.
Ok, I probably was wrong, but I have a strong feeling FOR-NEXT can do stupid things with the stack, in particular in combination with GOSUB.
Here are two examples which you probably wouldn't expect to work, and neither they do:
10 FOR J=1 TO 10
12 IFJ>1 THEN 20
15 GOSUB 50
20 NEXT J
50 PRINT J:GOTO 20
10 GOSUB 50
20 PRINT J:NEXT J
22 END
50 FOR J=1 TO 10
52 IF J>1 THEN 20
55 RETURN
Both GOSUB (RETURN) and FOR (NEXT) will move the stack pointer, but it seems the former combo has higher precedence and will make Basic "forget" about the loop just initiated.
I can see that a variation on at least the first example could show up in a bad program.
Here are two examples which you probably wouldn't expect to work, and neither they do:
10 FOR J=1 TO 10
12 IFJ>1 THEN 20
15 GOSUB 50
20 NEXT J
50 PRINT J:GOTO 20
10 GOSUB 50
20 PRINT J:NEXT J
22 END
50 FOR J=1 TO 10
52 IF J>1 THEN 20
55 RETURN
Both GOSUB (RETURN) and FOR (NEXT) will move the stack pointer, but it seems the former combo has higher precedence and will make Basic "forget" about the loop just initiated.
I can see that a variation on at least the first example could show up in a bad program.
Anders Carlsson
The BASIC Interpreter actually searches the stack for the proper value and throws away everything above it.
So, if you do a GOSUB, then a FOR, then a RETURN, the FOR will be discarded and the RETURN will jump to directly after the GOSUB.
In addition, if a variable is included with the NEXT, the Interpreter searches for that FOR in the stack. If not, then the next FOR is used.
But the most interesting thing it does is when you issue a FOR. Then the interpreter searches the stack for a FOR with the same variable, throws everything on top of it away, and replaces the old FOR with the new one.
That's why your FOR example didn't get an out of memoy error.
My 6 year old daughter wanted the smiley
So, if you do a GOSUB, then a FOR, then a RETURN, the FOR will be discarded and the RETURN will jump to directly after the GOSUB.
In addition, if a variable is included with the NEXT, the Interpreter searches for that FOR in the stack. If not, then the next FOR is used.
But the most interesting thing it does is when you issue a FOR. Then the interpreter searches the stack for a FOR with the same variable, throws everything on top of it away, and replaces the old FOR with the new one.
That's why your FOR example didn't get an out of memoy error.
My 6 year old daughter wanted the smiley
Is this true? I suspect the line length would matter. Can someone elaborate on this.Leeeeee wrote:When performing GOTO or GOSUB the interpreter can only scan forward through lines as there are no reverse links in the BASIC line structure.
The actual length of the line doesn't matter, a long line will be skipped over as quickly as a short line.
Lee.
If I recall correctly this is true. Each line of program is stored with a line number, and a pointer to the next line (so essentially it is one big linked list, forward only. I think the pointer was a 8-bit unsigned integer offset, so each line had a maximum of 254 chars).
So to scan for a specific line, the actual length of the line didn't affect the time for the search, but how far the line was from the beginning of the program did.
So to scan for a specific line, the actual length of the line didn't affect the time for the search, but how far the line was from the beginning of the program did.
Just pulled out my old Mapping the Vic book pg 307
The format of each line of basic in memory is the following:
Link-Field -- Line-number-- Tokenized Basic Line -- byte containing 0 for EOL
LSB MSB LSB MSB
The end of program contains a link field with 0 MSB LSB
So the link pointer is a two byte LSB MSB... So definately line length will not
affect goto or gosub speed.
I seem to remember some basic list protection where the author would
mess with these links to hide code in a program.
The format of each line of basic in memory is the following:
Link-Field -- Line-number-- Tokenized Basic Line -- byte containing 0 for EOL
LSB MSB LSB MSB
The end of program contains a link field with 0 MSB LSB
So the link pointer is a two byte LSB MSB... So definately line length will not
affect goto or gosub speed.
I seem to remember some basic list protection where the author would
mess with these links to hide code in a program.