RAM efficient programming - How?

Started by John Ely June 11, 2004

Hello,

This question is a rather broad and open ended one.

I've heard bits and pieces of programming advice, but I wonder if there is
an article someplace with the basics of conserving RAM (particularly
keeping the stacks low). How do I best keep stack space from growing.

For example, some say never exit a sub inside an If - End If.

Also, never exit a sub inside a Do loop.

Do these things really help or not? Why?

Regards,

John



--- In , John Ely <kwradio@c...> wrote:
> This question is a rather broad and open ended one.
> [...] How do I best keep stack space from growing.
>
> For example, some say never exit a sub inside an If - End If.
> Also, never exit a sub inside a Do loop.
>
> Do these things really help or not? Why?

In order to reduce RAM usage one must understand what contributes to
RAM consumption. First, and perhaps most obvious, is the RAM used by
statically allocated variables. These are the variables declared
outside of any subroutine or function. It doesn't matter whether the
statically allocated variable is public or private, it takes the same
amount of RAM space. A Byte variable consumes 1 byte of RAM,
Integers take 2, Longs and Singles take 4. Arrays of these types
consume the product of the number of elements and the basic size.

Strings take two bytes plus the maximum string length. What the
maximum length is depends on what type of string it is. The
typically used string type with the default BasicX IDE string maximum
setting will take up 22 bytes. See the BasicX Language Reference for
more information on the string types.

The second contributor to stack usage is subroutine and functioin
calls. Each "active" call eats up 9 bytes plus the size of
parameters passed plus local variables declared in the sub/function.
A Byte or Integer passed by value or any type passed by reference
uses up two bytes of stack space. A Long or Single passed by value
uses up 4 bytes of stack space. Strings are always passed by
reference (even if you specify ByVal).

Now, what do I mean by "active". Well, consider this simple program:

Sub main()
Call Sub1()
End Sub

Sub Sub1()
Call Sub2()
End Sub

Sub Sub2()
Dim b as Byte
End Sub

When this program is run and Sub2 is executing, main, Sub1 and Sub2
are all active. So you see that nesting subroutines or function
calls requires stack space equal to the sum of the stack requirements
of all of the nested calls. If you recursively call a subroutine
(e.g. Sub1 calls itself either directly or indirectly) it has the
cumulative impact on stack usage.

Tasks are another major consumer of stack space. Each task requires
its own stack of which 15 bytes are used for the initial task
invocation. (Note that technically, main() is actually a task and
therefore uses up 15 bytes plus local variables when it runs). The
task stacks are simply arrays of bytes that are allocated either
locally or statically and therefore consume memory in the same manner
as any other Byte array.

From this short description, one may conclude that the following
ideas may help reduce RAM usage:

- set the default string size to the absolute practical minimum

- use strings very sparingly and when you do, try to use the String*N
style, fitting the length to minimum necessary

- minimize subroutine/function nesting (it may make sense to
duplicate code instead of calling, i.e. trade ROM space for RAM space)

- use the smallest data types that will serve their intended purpose

- use the smallest dimensions on arrays that will serve your purpose
and avoid using multi-dimension arrays

- where possible, especially in subroutines, re-use variables for
other purposes when they're no longer needed instead of declaring new
ones

- consider putting larger data items that are constant or
infrequently updated in program memory or EEPROM.

To answer your last question specifically, it makes no difference how
you exit a subroutine or function. The stack, by necessity, is
cleaned up when the subroutine exits no matter what path is taken.
If it weren't, you wouldn't properly return to the point of call.