This is often forgotten, but since v5, there has been a loop-control-object version of GetBalance and GetMovement, which operate on an already-loaded ledger record in the special case of a report for-loop on the Ledger table.
Normally, GetBalance() and GetMovement() are fairly high cost functions, because they must first perform the supplied search to find and load the matching Ledger records.
The loop-object version (in the form L.GetBalance(period), with no search expression parameter, where L is a loop control variable for a Ledger loop) already has the relevant ledger record loaded, so is virtually zero-cost.