TRS-80 Pocket Computer - Optimizing execution speed of Basic programs
Lessons from timing events that take place during Basic programs execution
When programming was more like a cooking recipe, and not an automated generation of bugs...
The TRS-80 Pocket Computer was the first pocket computer ever in the early 1980's. You could input and execute Basic programs. Bringing the power of both a calculator and a Basic-speaking computer in your pocket (let's say in your "shirt pocket" not in your "back pants pocket", so not to crunch the LCD of the TRS-80 Pocket Computer) was great.
Now that we only speak of Gigahertz processors, of huge amounts of bugs in millions of lines of code, let's remember the old days with this TRS-80 Pocket Computer... when :
- Programs had to hold in about 1 KB of memory. Optimization of resources, variables and code was a big part of the programming's job.
- Execution time of these programs had to be as "less slow" as possible. Tricks had to be found to speed up the processes. As an example, you can give a look to my Random Number Generator page where you will discover that out of two very wise formulas for generating random number, one can take as much as half the time of the other one.
In this web page, we will focus on learning how to time TRS-80 Pocket Computer Basic statements as they are performed and on finding amazing change factors in operation time according to the TRS-80 Pocket Computer's specifications.
How to time TRS-80 Pocket Computer Basic statements
How fast is the TRS-80 Pocket Computer? The only way to find it out is to have it do a task and time how long it takes. But the TRS-80 Pocket Computer has no inbuilt clock and it comes without the common TIME$ Basic statement.
So, you will need to work with an external timer. Get ready to press at the same time the timer start button and the ENTER key of the TRS-80 Pocket Computer after having typed in the RUN instruction. Put a BEEP statement at the end of the program's execution, a beep always being easy to hear when a visual alert message (like "STOP TIMER") on the screen display of the TRS-80 Pocket Computer will require you more attention. When the beep occurs, stop your timer and you will have the operation time of your program.
Now, try timing this program which assigns a value to variable A :
Did it go by too fast to time? Yes, short tasks are completed so fast that you cannot distinguish between the start and the end of the task.
So, we will have to use a trick to measure operation time of the TRS-80 Pocket Computer. We will slow it down by making it do the same task hundreds of times. Therefore, we will need a FOR / NEXT loop. We will insert statements in between the FOR and NEXT instructions (in line 20 that we will leave blank for the moment).
So, let's build an "empty" FOR / NEXT loop and time it :
10 FOR Z = 1 TO 500 30 NEXT Z : BEEP 1 : END |
It took about
90 seconds on my TRS-80 Pocket Computer.
Now, if we input a Basic instruction in line 20, A = 1 for example, we will be able to know how much time it takes to execute it 500 times by substracting 90 to the timer's result. Deviding the found number by 500 will give us the operation time of one line 20 statement.
How timing helps us find good programming rules for the TRS-80 Pocket Computer...
Let's go back to our previous program and change variable Z by variable A :
10 FOR A = 1 TO 500 30 NEXT A : BEEP 1 : END |
Run the revised program and time it again. How long did it take? I am quite sure that the immediate answer that came to your mind was : "it should take the same time than before, about 90 seconds".
But, it took about
115 seconds on my TRS-80 Pocket Computer.Yes, it took longer this time! Why should the program
slow down by almost 28% when A is used instead of Z?
And this is how we discover a first interesting thing : the TRS-80 Pocket Computer searches for the variables from the last (Z) to the first (A) in memory. Therefore, it takes less time to access variable Z than variable A. You should then remember the following rule if you wish to speed up programs that access a variable many times :
General rule #1 : Use letters at the end of the alphabet for variables that are often accessed in a program
Time this other program :
10 FOR Z = 1 TO 500 20 GOTO 30 30 NEXT Z : BEEP 1 : END |
It took about
130 seconds on my TRS-80 Pocket Computer.
Now, time this other one, where we just inserted line 25 with a REM comment in the previous program code :
10 FOR Z = 1 TO 500 20 GOTO 30 25 REM 1234567890 30 NEXT Z : BEEP 1 : END |
This time, it took about
140 seconds on my TRS-80 Pocket Computer.
What? A never executed comment line increases execution time? Yes, indeed, it does due to the fact that the program does not know exactly where line 30 is when line 20 tells it to go there. So it starts searching forward for line 30 and it therefore screens line 25 (memory block by memory block so called "step" in the MEM instruction of the TRS-80 Pocket Computer) before finding line 30.
Let's illustrate it with a memory table of the program as it is encoded in the RAM of the TRS-80 Pocket Computer :
x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|
00 + x | 10 | : | FOR | Z | = | 1 | TO | 5 | 0 | 0 |
---|
10 + x | EOL | 20 | : | GOTO | 3 | 0 | EOL | 25 | : | REM |
---|
20 + x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
---|
30 + x | EOL | 30 | : | NEXT | Z | : | BEEP | 1 | : | END |
---|
40 + x | EOL | | | | | | | | | |
---|
To get fully convinced of this explanation of the program pointer moving back and forth in the program memory, time the next program :
10 FOR Z = 1 TO 500 20 A = 1 25 Y = 1 30 NEXT Z : BEEP 1 : END |
It took about
160 seconds on my TRS-80 Pocket Computer, to be compared with the execution time of this other program :
10 FOR Z = 1 TO 500 20 A = 1 : Y = 1 30 NEXT Z : BEEP 1 : END |
It "only" took about
155 seconds on my TRS-80 Pocket Computer, to be compared with the 160 seconds of our the same program but splitted in two lines.
So, you should avoid REM comments. As well, avoid change of line and rather choose ":" linking between instructions, even though reading of programs is not eased doing so.
General rule #2 : Keep your program code as compact as possible
Calculating execution time of a single Basic statement - Examples of Basic statements optimizations
Calculating execution time of a single Basic statement
Go back to our timing program and find out how long it takes to load a number into the memory assigned to variable A (Remember? That was our first try...) :
10 FOR Z = 1 TO 500 20 A = 1 30 NEXT Z : BEEP 1 : END |
It took about
130 seconds on my TRS-80 Pocket Computer. Since the empty FOR / NEXT loop takes about 90 seconds, assigning 500 times 1 to A takes about : 130 - 90 = 40 seconds. One operation of assigning value 1 to variable A takes : 40 / 500 =
0.08 second (80 milliseconds).
Now let's see how long it would take to assign value 1 to variable Y :
10 FOR Z = 1 TO 500 20 Y = 1 30 NEXT Z : BEEP 1 : END |
It took about
120 seconds on my TRS-80 Pocket Computer.
So Y = 1 is executed in : ( 120 - 90 ) / 500 =
0.06 second (60 milliseconds).
Here again, we see that the TRS-80 Pocket Computer can work with variables at the end of the alphabet much faster than it can with variables at the beginning of the alphabet. So, remember to use the same letters for variables to compare two close statements.
Examples of Basic statements optimizations by timing a few TRS-80 Pocket Computer Basic statements
Now, we will show the result of timing a few statements put in line 20 of our test program.
Operations :
Basic statement | Timer (sec) | Single operation (sec) |
---|
Y = 3 + 2 | 135 | 0.09 |
Y = 3 - 2 | 135 | 0.09 |
Y = 3 * 2 | 140 | 0.1 |
Y = 3 / 2 | 155 | 0.13 |
Doing so, you will discover that addition and substraction run just as fast, that multiplication will require more time to execute and division is the slowliest of the four basic operations.
As a consequence, we see that we'd better type in Y = 3 + 3 than Y = 3 * 2 in a code line.
Also try :
Basic statement | Timer (sec) | Single operation (sec) |
---|
Y = 2 * 2 * 2 * 2 * 2 | 205 | 0.23 |
Y = 2 ^ 5 | 325 | 0.47 |
Yes, result stored in Y is the same in both cases, but the first statement runs twice faster than the second one. By the way, it means that the ROM program for the Basic Power instruction (^) is not optimized at all...
Arrays :
Basic statement | Timer (sec) | Single operation (sec) | Equivalent to... | Timer (sec) | Single operation (sec) |
---|
A(1) = 1 | 160 | 0.14 | A = 1 | 130 | 0.08 |
A(2) = 1 | 155 | 0.13 | Y = 1 | 120 | 0.06 |
A(27) = 1 | 155 | 0.13 | | | |
A(170) = 1 | 160 | 0.14 | | | |
If you remember that A(1) and A, A(25) and Y are stored in the same memory cell, then it is always about twice faster to use alphabetical variables than arrays for those 26 memory storages.
Putting a number in a variable :
Basic statement | Timer (sec) | Single operation (sec) | Equivalent to... | Timer (sec) | Single operation (sec) |
---|
Y = .1 | 125 | 0.07 | Y = 0.1 | 126 | 0.072 |
| | | Y = E-1 | 128 | 0.076 |
Y = 1 | 120 | 0.06 | | | |
Y = 10 | 124 | 0.068 | Y = E1 | 124 | 0.068 |
Y = 1000 | 130 | 0.08 | Y = E3 | 124 | 0.068 |
| | | Y = 10 ^ 3 | 234 | 0.288 |
Y = 1000000 | 140 | 0.1 | Y = E6 | 124 | 0.068 |
| | | Y = 10 ^ 6 | 235 | 0.29 |
Here again, we demonstrate that the power (^) is very slow when the Exponent function (Exp key, shown as a special E character) was much better programmed by the very ingenious engineers of the TRS-80 Pocket Computer.
Also, try to write 3E2 instead of 300 or 7E4 instead of 70000 in your programs!
I now let you time your own pieces of Basic coding. For those of you who still like to play with their TRS-80 Pocket Computer, I am very sure that you will find out how to speed up your programs by unbelievable numbers of milliseconds that, once all added together, can bring your programs to run much smoother.