From 6e4c1edfda8ca5765fa08d8f7faf1ccdce0a034a Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Tue, 13 Jan 2026 20:24:48 -0400 Subject: [PATCH] docs: add programming rules Signed-off-by: Ronald Caesar --- docs/PROGRAMMING_RULES.md | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/PROGRAMMING_RULES.md diff --git a/docs/PROGRAMMING_RULES.md b/docs/PROGRAMMING_RULES.md new file mode 100644 index 0000000..dd5c66c --- /dev/null +++ b/docs/PROGRAMMING_RULES.md @@ -0,0 +1,84 @@ +# 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. +```