Files
archived-ballistic/docs/PROGRAMMING_RULES.md
Ronald Caesar 6e4c1edfda docs: add programming rules
Signed-off-by: Ronald Caesar <github43132@proton.me>
2026-01-13 20:32:41 -04:00

85 lines
1.9 KiB
Markdown

# Compiler Aliasing Fear
Pointer aliasing forces compilers to avoid optimizing code as best as they should. We use local variables to fix this problem.
For all examples shown below, assume `context` is a pointer to a heap allocated
struct.
## Rule 1: Scalars and Arrays inside a loop
If booleans, integers, or arrays gets modified inside a loop, always assign them to
local variables before doing so. Yes there are other ways to make the compiler
optimize this specific scenario, but we do this to guarantee speed.
```c
// SLOW
//
for (int i = 0; i < 100; i++) {
// Forces a write to memory 100 times.
//
context->counter++;
// This stopped context->counter from being kept in a CPU Register..
//
// The compiler doesnt know what do_something() does. It might write to
// context->counter.
//
// The compiler is forced to:
//
// 1. Save `counter` from register to RAM.
// 2. Calls do_something().
// 3. Reloads `counter` fron ram to register.
//
do_something(context)
}
```
### Register Only Approach
```c
// FASTEST
// Load from memory to register once.
//
uint32_t count = context->counter;
for (int i = 0; i < 100; i++) {
// Modify register directly. Zero memory access here.
//
count++;
// Pass the register value to the helper. We pass 'count' directly to avoid
// the memory store.
//
do_something(context, count);
}
// Store from register back to memory once.
//
context->counter = count;
```
### Hybrid Aprpoach (Optimized Load, Forced Store)
We do this if we cannot change the function signature.
```c
// FAST
uint32_t count = context->counter;
for (int i = 0; i < 100; i++) {
count++;
// We must sync memory because do_something() reads it. This costs us 1
// STORE per loop, but we still save the LOAD.
//
context->counter = count;
do_something(context);
}
// No final store needed, we kept it in sync the whole time.
```