Skip to content

Reducing Code Size

Tom Sherman edited this page Feb 26, 2017 · 30 revisions

Determining Minimum Size of the Heap

In order to use malloc, calloc, realloc it is necessary to call initHeap located in system.h. This is typically done a single time at the beginning of your program. A typical example would be:

static char heap[10000];
static bool init_run = false;

void init()
{
    initHeap(heap, heap + sizeof(heap));
    ...
}

void _main()
{
    if (!init_run)
    {
        init();
        init_run = true;
    }

    ...
}

This would create a 10,000 byte heap for the rest of the program to use. Any call to malloc, calloc, realloc will allocate a pointer somewhere in the memory region that heap[10000] occupies. If initHeap is called again, the new heap will be used and the previous one will be essentially forgotten about.

When the heap is initialized this way, the heap is contained in one of the memory regions supplied in the config file for wiimake. This means that an excessively large heap will limit how much code you can inject. On the other hand, a heap that is too small will result in many failed calls to malloc, calloc, realloc. (Note: in order to write stable code you should always handle the possibility of memory allocation failing). Thus, it is extremely important to choose the 'right' size of your heap. Here are some steps you can take to determine how large the heap should be.

First, you must account for the memory that is allocated internally in the MeleeModdingLibray.

print() located in print.h - The stream that print writes to is allocated on the heap. Memory is not allocated until print() is called (note: calling error() does not cause memory to be allocated). Each line of display takes up 96 bytes. The maximum number of lines is capped at 27 or until the stream takes up 1/5 of the heap size. Thus, 96 * num_lines * 5 = size_of_heap. This means, in order to have all 27 lines available to display you need a heap of size: 96 * 27 * 5 = 13000. Note that this does not take into account the fragmentation that occurs when malloc and free are frequently called. It is important to take this into account and make the heap larger than necessary. More exact info will be provided when testing is done, but for now assume that you will a heap twice as large as the total memory allocated. This consideration is naturally built in the way print allocates memory. By limiting its size to 1/5 of the total heap, it is unlikely to have invalid allocations.

addLogic, addMove located in AI.h - The logic and inputs stored in an AI struct are kept on the heap. This takes up a considerably smaller portion of the heap than print() but still needs to be accounted for. Each Logic struct has a size of 24 bytes. The size of the array holding the AI's logic increases as follows: 1, 3, 7, 15, 31. So if your AI has to hold 16 logic rules at one time, it will occupy 31 * 24 = 744 bytes on the heap. Each ControllerInput has a size of 8 bytes and the array is allocated the same way as the logic one. Thus, if the largest move used has 15 distinct inputs, the input array will have size 15 * 8 = 120 bytes. Both of these size estimates are more than large enough for most programs. The total recommended size then is 2 * (744 + 120) = 1728.

If your code never calls print then 1728 bytes is the recommended size of your heap. If print is called you will need a bigger heap (unless you use less than 5 lines), but remember 4/5 of the heap is unused by print and that should be enough for the AI in most cases.

One possible way to avoid this whole problem is to shrink Melee's heap and use that space, for more information see the next section titled "Shrinking Melee's Heap".

Shrinking Melee's Heap

Compiler Optimization

Function Size