| PICAXE Optimisations | |
|
Tips & Tricks for Making Your Code Fit | |
|
Before considering optimisations for PICAXE code, it is important to realise
that most
optimsations will make your program code less readable, less understandable,
less maintainable, and harder to debug than unoptimised code. It is therefore recommended that all but the simplest optimisations should be left until your code has been completed, tested and debugged. This may not however always be possible when you are running out of program code space before your code is finished. In such circumstances it is recommended to apply as few optimisations as possible to make your program code fit. Unless there is a real need to perform optimisation it is often better practice to leave the code unoptimised. And if you do implement any optimisations it is wise to document what you have done, and why, in your program source code, if you wish to understand the code when you return to it at a later date, or pass it on to another PICAXE programmer.
Most of the lower-end PICAXE's are pretty constrained devices when it comes to
the amount of program code which they can hold; quite often just 128 or 256 bytes
of program which may have to be shared with EEPROM data. It can therefore be a
challenge to make your program fit into the available space - There is nothing
worse than adding a single line of code only to find that you have suddenly
exceeded the maximum amount of memory available. The art of making a program fit into the available space is called
'optimisation', and there are two sorts; optimising for speed of execution, and
optimising for minimal program code space. The two are often mutually
exclusive as a compact program, usually with loops, needs all those loops
unrolling into repeated sequential code to gain a speed advantage, and
vice-versa. For the PICAXE however, it is quite common that optimisations to
reduce code space will also speed up the program because of the way the
PICAXE holds its program in memory. Unlike a tradional microcontroller where a single instruction takes multiple
numbers of locations in program code memory, the PICAXE uses a system where
the instructions are not of a fixed size. This means that the shorter the
instructions it needs to use the more that can be fitted into the available
code memory, and the shorter the instructions, the quicker ( in general )
they are to execute. The optimisations detailed here are all designed to reduce the program code
size and hopefully increase the speed of execution. Speed may not always be
increased though because, if an instruction is placed into memory such that it
overlaps two consecutive memory locations, and that's quite likely, then it
will take slightly longer to execute. The loss of execution speed may be
regained elsewhere by other optimisations which improve it, and it is a small
price to pay to make the program fit which is often far more important than
the raw execution speed.
Suppose you want to take the inputs from Pin 6 and Pin 5 and turn these into
a value in 'b0' of between 0 and 3. One obvious solution would be ...
It works, but the '%01100000' is a number which requires 11 bits in total to
represent it. If we do the division before the '&' masking we can use ...
It still works, but because '%00000011' only requires seven bits, we just saved
four bits ( over 30% ) which can be used elsewhere. Using pins 0 or 1 adds just four bits to the instruction whereas using pins 3 to
7 adds seven bits. Designing your hardware so your most commonly refered to pins
within the code are Pin 0 and Pin 1 can have a significant effect accross a
complete program.
can be rewritten as ...
Although the initial assignment into 'w2' adds extra code, the net result is
a saving of bits. Of course, the more frequently a number is used, the greater
the saving in total.
The only real optimisation which can be achieved is in assignments where the
variable being assigned to also appears as the first variable immediately on the
right hand side of the '=' ...
are more program code space efficient than ...
Can be replaced by ...
All the 'FOR' part of the FOR..NEXT loop does is to initialise what would be
an index variable with the value which appears immediately after the '=', the
value after the 'TO' is redundant and could actually be anything at all. The
clever part of the FOR..NEXT loop is all associated with the 'NEXT', which we
won't be using, but the 'FOR' part assignment is a lot shorter than the proper
way of doing it. There are two problems with this optimisation; firstly it is not easily seen
that it is an optimisation and not some coding error without some additional
commentary explaining it, and it will not work if you do need to use FOR..NEXT
loops in your program, as the compiler is likely to complain when you do a
Syntax Check or attempt a download.
will generate four completely redundant additions of zero, even though they will
not have any effect upon the value assigned to 'b0'. The same occurs if you
divide or multiply by one and in a number of other cases.
While it is relatively easy to spot redundant operations such as the example
above, it is much harder to spot them when named constants defined by 'SYMBOL'
are used ...
In this case there is no redundancy, but if M were zero or one, or C were zero
then there would be redundancy. Named constants are a double-edged sword; while making your code more readable
and modifiable, they can also have the unwanted side-effect of using up excess
program space. The choice between using named constants or not is not an easy
one to make.
The PICAXE does not allow 'b0=-1' and this has to be derived by using 'b0=0-1'
as in the above example. This uses an unnecessary 9 bits of program code. When
we are subtracting one constant from another, we will end up with another
constant, so by pre-caclculating that result ( 0-1 is $FF in this case ) we
can simply replace the 'b0=0-1' with the equivalent constant, 'b0=$FF'.
The same pre-calculations can be applied whenever the PICAXE is being instructed
to perform constant arithmatic itself, for example ...
becomes ... This technique is commonly known as "Constant Folding".
There are special cases to be careful of ...
In this example it would appear that multiplying by a number and then dividing
by that same number is a pointless operation which achieves nothing, however
there is much more to it than first meets the eye.
All arithmatic is performed using 16-bits, and a large multiplication, as above
will cause a result greater than 16-bits. Those bits above the bottom 16-bits
are lost, and this can often be used to great effect. The above code is actually
a very poor way to do 'b0 = b0 & 1', and constant folding would not give
the result expected. The same issues can apply with division followed by multiplication ...
This is equivalent to 'b0 = b0 & $FC', and again, constant folding will
not give the result desired.
Using the PICAXE programming language, a reasonable implementation of the
example is as follows ...
This can be optimised to ...
The same technique can be used when setting or clearing Digital Outputs but
care must be taken to ensure that 'glitches' caused by setting the Output
incorrectly before setting it correctly does not cause problems with the
attached hardware ...
The only significant savings can be made where a FOR..NEXT loop takes an index
variable between two pre-defined numbers, which is the case with most ...
The FOR..NEXT loop is defined as 'FOR b0 = 1 TO 16' because the loop needs to
be executed sixteen times; loop 1, loop 2, and so on through to loop 16.
Because the index variable 'b0' is not actually used within the loop, and
is effectively used as a simple count of how many times it has gone through the
loop, it could equally be '11 TO 26' or '2 TO 17', but the optimum is '0 TO 15',
because, as we know, the program code space taken up by numbers depends upon
the value of the number and 0 and 1 use less program code than 2 and above, and
2 to 15 use less code space than 16 and above.
Although the change of the starting value from 1 to 0 makes no saving, changing
16 to 15 saves four bits. It is not always easy to optimise the FOR.NEXT values, especially where the
index value needs to be used within the loop, but you will be coding for
maximum space saving if your FOR..NEXT loop starts with value 0, and failing
that, at value 1.
Should simply become ...
Duplicated strings are however another entirely different matter, and are most
likely to occur when using the 'SEROUT' command to send information to an LCD
or PC ...
Reasonably long duplicated strings may be removed into a separate, common
routine while still producing a net saving of program space ...
becomes ...
The 'w0=w0*2' and RETURN are common to both routines, and as the assignment to
'w1' is not dependant upon 'w0' the two routines can be re-ordered and
optimised ...
If you have any statements which are there because they, "may be used later",
but are not presently used, then it is wise to comment them out of the code
until they are needed. It can often be difficult to spot redundant code,
especially when it is a subroutine which is never called, and was written when
you thought you may need it.
PICAXE is a trademark of Revolution Education Ltd.
These PICAXE pages are produced entirely independantly of Revolution Education
Limited and may not reflect the opinion of Revolution Education Limited or its
agents. The information provided is based upon and derived from information
published by Revolution Education Limited, other sources of PICAXE information
and the author's own experiments and prior experience. The views expressed by
the author do not necessarily represent those of Revolution Education Limited or
its agents. While every effort has been made to ensure that the information on
these PICAXE pages is accurate and correct, the author can accept no
responsibility for any errors or ommissions which do occur. The information
provided is used entirely at your own risk.
| ||||
|
|
First published on Thursday the 25th of February, 2004 at 14:38:14
Last upload was on Monday the 23rd of August, 2004 at 00:20:47 |