Cost of BASIC operations

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Cost of BASIC operations

Post by Hedge1970 »

While trying to optimise some BASIC code, I was wondering if there is a guide to the cost of actions? My stuff is all pretty simple operands, IFs variables etc.

A couple of examples of what I mean. Is it easier for the machine to...

1) Poke to an address or LET A=Value
2) Is IF A=1 easier than IF A <> 1

There are quite a few similar options in BASIC some of which I probably have not come across yet but in general is there a look up anywhere that would help me understand the best most efficient use of instructions?

hope this is not too obvious, google does not turn up anything useful and search is tuff with this type of subject.
User avatar
djnzx48
Manic Miner
Posts: 730
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: Cost of BASIC operations

Post by djnzx48 »

I would say POKE to an address is definitely faster than using LET to assign a variable, because POKE has the address already available (assuming you're not using complex calculations to compute it), whereas a LET statement has to search BASIC memory for a variable with the correct name, create it if it's not there, etc. Variables that are assigned to earlier in the program should be quicker to use in general. There's probably very little difference in execution speed between A=1 and A<>1, as essentially it's doing the same basic operation.

For general instruction times, have a look at this document: http://www.zx81.nl/dload/zx81-memory.pdf. It's for the ZX81 not the Spectrum, and the aim there is saving memory so the techniques suggested there will more likely slow your program down. But there's a nice table with the timing of different expressions that might be useful to you.
User avatar
ZXDunny
Manic Miner
Posts: 498
Joined: Tue Nov 14, 2017 3:45 pm

Re: Cost of BASIC operations

Post by ZXDunny »

Use BASinC. It has a profiler for BASIC code that will tell you how many T-States each statement/function takes.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Cost of BASIC operations

Post by Hedge1970 »

djnzx48 wrote: Tue Mar 12, 2019 10:31 pm I would say POKE to an address is definitely faster than using LET to assign a variable, because POKE has the address already available (assuming you're not using complex calculations to compute it), whereas a LET statement has to search BASIC memory for a variable with the correct name, create it if it's not there, etc. Variables that are assigned to earlier in the program should be quicker to use in general. There's probably very little difference in execution speed between A=1 and A<>1, as essentially it's doing the same basic operation.
Thank you for that it makes perfect sense, I am still trying to understand my game loop, currently ive optimised the variable names to single letters and can still "read it" - from descriptive syntax. However once its all working inside the loop I will replace all the LETS with POKES and PEEKs for the final version.

djnzx48 wrote: Tue Mar 12, 2019 10:31 pm For general instruction times, have a look at this document: http://www.zx81.nl/dload/zx81-memory.pdf. It's for the ZX81 not the Spectrum, and the aim there is saving memory so the techniques suggested there will more likely slow your program down. But there's a nice table with the timing of different expressions that might be useful to you.
I had a quick look but will need to read more on that at first glance it seems powerful and exactly what I was asking but then I looked at the expressions and there seem to be a lot missing, but a great resource so thank you again.
ZXDunny wrote: Tue Mar 12, 2019 11:27 pm Use . It has a profiler for BASIC code that will tell you how many T-States each statement/function takes.
Thanks looks useful, Ive only used spin and spectaculator but not really got into ether. my programming is done on a speccy with a printer and pens and paper :0) But i know i should make better use of whats available in the 21st Century!
User avatar
Joefish
Rick Dangerous
Posts: 2058
Joined: Tue Nov 14, 2017 10:26 am

Re: Cost of BASIC operations

Post by Joefish »

The only optimisations I know for BASIC are keeping the game loop short and very near the top of the listing. Maybe your first line is a GOTO or GOSUB to the game title screen, but the game loop should start immediately after. That's because a GOTO starts searching for its target line number from the beginning of the program every time; there's no look-up table to help it jump to the right bit of program. So the closer your program loop is to the start of the program, the quicker it is to go round the loop and GOTO back to the start.

And always use ATTR, never SCREEN$, to identify things on-screen. And ATTR is slightly quicker than calculating the attribute address and doing PEEK (for the same result).

There are also things you can do in a game where it might slow down momentarily (e.g. when the player collides with something, jump out to a routine outside the game loop that works out what you've collided with) but that the player is less likely to notice the pause if it involves them. Whereas a background event occasionally slowing the game down would be irritating.

Also, if you have multiple moving enemies on screen, you can store them in an array and only move one or two on each pass of the game loop, stepping through the enemy list over several passes. So each one ends up moving slower than the player, but it doesn't over-burden your game loop with trying to do them all at once.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Cost of BASIC operations

Post by Hedge1970 »

Joefish wrote: Wed Mar 13, 2019 3:15 pm The only optimisations I know for BASIC are keeping the game loop short and very near the top of the listing. Maybe your first line is a GOTO or GOSUB to the game title screen, but the game loop should start immediately after. That's because a GOTO starts searching for its target line number from the beginning of the program every time; there's no look-up table to help it jump to the right bit of program. So the closer your program loop is to the start of the program, the quicker it is to go round the loop and GOTO back to the start.
Hey Joe, does this include the subroutines the game loop calls or can they stay put i.e would I need to move subs 600 and 700 up to the top?

100
SET UP and STUFF
499

500 Start Game Loop
520
530
540 GoSub 600
550 GoSub 700
560
570 GoTo 500

600 REM Sub routine
700 REM Sub routine

Cheers Paul
User avatar
ZXDunny
Manic Miner
Posts: 498
Joined: Tue Nov 14, 2017 3:45 pm

Re: Cost of BASIC operations

Post by ZXDunny »

Move everything you can to the top. The rule of thumb is that the more time a program spends in a particular routine, the further up it should be. As JoeFish says, you need to move your main game loop to the top, and use a GO TO (or ideally, a GO SUB) as the first line to jump further down for the title screen etc.

If your game loop calls a particular area of your program during every loop, consider moving that either into the game loop itself, or putting it above the game loop. Remember, any GO TO or GO SUB will first of all grab the line number you're wanting to jump to and then start at the first line - compare the line number to the one you want, and if it's lesser then it will jump to the end of the line and grab the next one, examine that... and rinse/repeat until it finds the line you want to get to.

If your game loops around with a GO TO at the end to get to the next loop, then you want to cut down on the number of lines it has to search in order to find where to go.

Edit:

Move lines from 500 onwards up above the setup, and GO TO your setup as the first line.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Cost of BASIC operations

Post by Hedge1970 »

Yeah just after I posted I thought actually the GOSUB is a stack pointer so should know exactly where it needs to go back to - not sure if it knows where it needs to go to though? I do hope this is the case as its a lot of code to move around and I don't have an easy way of doing it other than carefully re-typing each line to the new line
AndyC
Dynamite Dan
Posts: 1406
Joined: Mon Nov 13, 2017 5:12 am

Re: Cost of BASIC operations

Post by AndyC »

Hedge1970 wrote: Wed Mar 13, 2019 6:14 pm Yeah just after I posted I thought actually the GOSUB is a stack pointer so should know exactly where it needs to go back to - not sure if it knows where it needs to go to though? I do hope this is the case as its a lot of code to move around and I don't have an easy way of doing it other than carefully re-typing each line to the new line
If memory serves, the GO SUB stack just contains the line number and statement number to next go to, so BASIC still has to find the next line again within the program. I may be wrong however.
Ralf
Rick Dangerous
Posts: 2286
Joined: Mon Nov 13, 2017 11:59 am
Location: Poland

Re: Cost of BASIC operations

Post by Ralf »

Remember also that if you just want your Basic program to run faster, by using whatever means, then you may try compilation instead of optimisation ;)

There are compilers for Spectrum Basic available and although I have very little personal experience with it, I remember seeing them in action and it's big performance boost, 300%, 500% or so. I believe you'll never achieve similar improvement with optimisation tricks, maybe unless you change your algorythm.

Of course you may have a different goal - become a master of optimisation ;) Then my advice doesn't apply to you.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Cost of BASIC operations

Post by Hedge1970 »

Well I just bit the bullet and moved, renumbered, deleted and then edited Gosubs and Goto in 150 odd lines of code to get the game loop at the top of the page (along with the gosubs)... but its made a big difference :)

Still working on the game but this is a really good change so thank you for the push :D .
User avatar
djnzx48
Manic Miner
Posts: 730
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: Cost of BASIC operations

Post by djnzx48 »

I wonder if program speed could be improved by doing something like DIM a(100) at the start of a program to create a large array, and storing all of your numeric variables somewhere within that array. Essentially putting your own code in charge of memory management. Then the time spent looking up the variable name would be minimised, and the interpreter could just jump straight to where each variable is within the array from the subscript.
User avatar
Joefish
Rick Dangerous
Posts: 2058
Joined: Tue Nov 14, 2017 10:26 am

Re: Cost of BASIC operations

Post by Joefish »

Don't put your startup code at the top. Just one GOSUB or GOTO to your title screen. The title screen code should then flow into code to initialise the game before it returns, so your game loop starts at the second line of code in the listing.

Also use multi-statement lines (use ':') as much as you can to reduce your use of line numbers, which makes them easier for the interpreter to skip over. (But note that whether your line numbers go up in 1s or 10s, that makes no difference).

If a routine is called EVERY cycle of the game, then just write it into the main loop. Sub-routines that are called periodically should be placed just after the main game loop as they're not quite so important.

If you've got one major sub-routine that is sometimes needed and sometimes not, you can place it directly after your main program loop, and sometimes just let your program loop run on into it (by conditionally skipping the last GOTO). Then at the end of this 'sub-routine' is the same GOTO to go back to the start of your program loop.
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Cost of BASIC operations

Post by Hedge1970 »

Joefish wrote: Thu Mar 14, 2019 11:57 am Don't put your startup code at the top. Just one GOSUB or GOTO to your title screen. The title screen code should then flow into code to initialise the game before it returns, so your game loop starts at the second line of code in the listing.
Thanks for the clarification this is what i've implemented. Ive left a number of lines should I need them but as its stands its 10 - screen and set up 50 Main loop.
Joefish wrote: Thu Mar 14, 2019 11:57 am Also use multi-statement lines (use ':') as much as you can to reduce your use of line numbers, which makes them easier for the interpreter to skip over. (But note that whether your line numbers go up in 1s or 10s, that makes no difference).
This is absolute Gold dust! My main loop is approx. 85 lines but I believe I have the scope for removing around 20 lines - many or which are REMs but also some further optimisation based on your recommendation here and your previous recommendation to keep the loop as small/tight as possible.

Joefish wrote: Thu Mar 14, 2019 11:57 am If a routine is called EVERY cycle of the game, then just write it into the main loop. Sub-routines that are called periodically should be placed just after the main game loop as they're not quite so important.

If you've got one major sub-routine that is sometimes needed and sometimes not, you can place it directly after your main program loop, and sometimes just let your program loop run on into it (by conditionally skipping the last GOTO). Then at the end of this 'sub-routine' is the same GOTO to go back to the start of your program loop.
I have accidentally managed to avoid this in my design so all good.

Thanks again
User avatar
Joefish
Rick Dangerous
Posts: 2058
Joined: Tue Nov 14, 2017 10:26 am

Re: Cost of BASIC operations

Post by Joefish »

Yeah, REMs might help to make things more readable, but really they just slow things down.
spectron
Drutt
Posts: 25
Joined: Thu Mar 29, 2018 3:27 pm

Re: Cost of BASIC operations

Post by spectron »

Ralf wrote: Wed Mar 13, 2019 6:33 pm and it's big performance boost, 300%, 500% or so.
Way more than that for most compilers, especially those that only allow for Integer values (which is all you really need if you're doing a game)
User avatar
djnzx48
Manic Miner
Posts: 730
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: Cost of BASIC operations

Post by djnzx48 »

YS did some nice benchmarks here. If these results are representative of real-world programs then that does seem pretty speedy!
IgnaCoBo
Drutt
Posts: 18
Joined: Tue Nov 16, 2021 9:11 am

Re: Cost of BASIC operations

Post by IgnaCoBo »

Hi,
I might be 2 years late, but, check these tips I use to improve speed in my Basic programs:
viewtopic.php?f=3&t=5535&p=78880&hilit= ... ack#p78880
megaribi
Drutt
Posts: 15
Joined: Thu Nov 25, 2021 4:31 pm

Re: Cost of BASIC operations

Post by megaribi »

Ralf wrote: Wed Mar 13, 2019 6:33 pm There are compilers for Spectrum Basic available and although I have very little personal experience with it, I remember seeing them in action and it's big performance boost, 300%, 500% or so. I believe you'll never achieve similar improvement with optimisation tricks, maybe unless you change your algorythm.
I have checked many Spectrum BASIC compilers, and the best is Hisoft BASIC. At first, it seems a bit harder to use than MCoder 2, because you need to add REM : OPEN # command at the program beginning, and declare as much variables to be integer as possible adding command REM : INT I,J,K,SUM but, you will get very fast standalone machine code program (not dependant on compiler in memory).

If you wish to stick with interpreted BASIC I have some more hints to speed it up.
  • Do not define UDG using READ ... DATA. Better, define them separately, and just load them using LOAD "" CODE USR "A" .
  • Define strings at the program initialization to maximal length using DIM A$(256) to avoid insertion
  • Do not try to reduce memory usage by LET A=VAL "30". Stick with plain LET A=30
  • Instead IF A=5 AND B=5 THEN ... better do IF A=5 THEN IF B=5 THEN ....
  • Try to prepare program/game background in one long string like

Image
Then one PRINT A$ will display your background in a blink of eye.
  • Put the most time critical code at the start of the program
  • First assign values to the most commonly used variables
  • Use single letter variable names where possible
  • ATTR is faster than SCREEN$
  • Do not repeat IF INKEY$="..." for every key. Better do LET V=CODE INKEY$ and compare v variabble with ASCII codes
  • BEEP stops program completely. To avoid mute game, use some OUT 254,n commands
  • When possible, use multiplication instead division. LET A=.5*B is 10% faster than LET A=B/2
  • Pre-calculate constants. Better write 1.5*A instead 3/*A/2
  • Avoid transcendent functions. Steven Vickers, although he had PhD in mathematics, generated Chebyshev polynomials for every calculations, although he had to do it only once statically. Table of possible values is better solution
Post Reply