While profiling an update loop in my MMBasic game I noticed a pattern that shows up in the code over and over: the same array index gets resolved five or six times per iteration. Looks trivial. Hoisting it into a variable still bought me real frame time, and as a side effect the code reads better.

The pattern

My update loop for active asteroids looked roughly like this:

FOR idx = 0 TO active_count - 1
  IF aactive(active_list(idx)) THEN
    INC ax(active_list(idx)), avx(active_list(idx))
    INC ay(active_list(idx)), avy(active_list(idx))
    INC arot(active_list(idx)), arot_speed(active_list(idx))
    IF arot(active_list(idx)) >= 360 THEN INC arot(active_list(idx)), -360
    ' ... more references to active_list(idx) ...
  END IF
NEXT idx

active_list(idx) shows up five or six times per iteration. Every reference goes through the interpreter’s array indexing machinery. But the value is constant for the duration of the iteration, so reading it once is plenty.

Hoist it once, use the variable

The refactor is trivial:

FOR idx = 0 TO active_count - 1
  ast_idx = active_list(idx)
  IF aactive(ast_idx) THEN
    INC ax(ast_idx), avx(ast_idx)
    INC ay(ast_idx), avy(ast_idx)
    INC arot(ast_idx), arot_speed(ast_idx)
    IF arot(ast_idx) >= 360 THEN INC arot(ast_idx), -360
    ' ... rest of the loop body uses ast_idx ...
  END IF
NEXT idx

ast_idx is just an integer variable. The interpreter reads it faster than it resolves active_list(idx) each time. One iteration won’t notice. Thousands per second do.

This single change alone bought back a few percent of frame time across my update and collision loops, and the line reads better as a side effect because ast_idx says what’s meant. In loops that only run a handful of times per frame, the effect just isn’t there — neither the cost of caching nor the win. So I do this where there’s real repetition and skip it everywhere else.