Skip to content

GPSL5I construction: preallocate code matrix and speed up gen_l5i_code (LFSR) #78

Description

@giove-a

Summary

GPSL5I builds its 10230×37 code matrix at construction via gen_l5i_code per PRN. Profiling (done while implementing Galileo E5a, which uses a similar LFSR generator) shows two independent optimization opportunities in src/gps/l5i.jl:

  1. mapreduce(…, hcat, 1:37) accumulation in read_gpsl5i_codes — modest time cost, large allocation cost.
  2. gen_l5i_code internals — the actual dominant cost.

Neither is on a runtime hot path (gen_code!/get_code operate on the prebuilt matrix); this only affects one-time construction. Filing for visibility — not urgent.

Measurements (read_gpsl5i_codes, 37 PRNs, steady-state median)

variant time allocations
current mapreduce(…, hcat, …) 3.45 ms 7.57 MB
preallocated column-fill 3.03 ms 0.76 MB
gain 1.14× (~0.42 ms) 10× (−6.8 MB)

So the hcat accumulation (which reallocates a growing matrix 37 times) leaves only ~0.42 ms / ~12% on the table time-wise, but ~6.8 MB of needless allocation churn.

The bigger opportunity: gen_l5i_code

The hcat is not the bottleneck — gen_l5i_code is. Pure LFSR work is ~3.0 ms (37 × ~81 µs/PRN). For contrast, the new Galileo E5a generator produces 50 PRNs in ~0.7 ms, while L5-I produces 37 in ~3 ms — roughly 5–6× slower per code.

Likely causes in gen_l5i_code / shift_register:

  • Per-call allocation of tap-index arrays ([9, 10, 12, 13], [1, 3, 4, 6, 7, 8, 12, 13]) and the weight vector [4096, …, 1].
  • Array/index-based shift register instead of bitmask taps.

The E5a generator (src/galileo/e5a.jl, _e5_lfsr_bits) uses bitmask taps with count_ones for the feedback parity, which is much faster. Porting L5-I to the same style would recover most of the ~2.5 ms.

Suggested fix

  1. Preallocate the matrix in read_gpsl5i_codes and fill columns in place (10× allocation drop, ~0.4 ms).
  2. Rewrite gen_l5i_code/shift_register to a bitmask LFSR (hoist the constant tap masks out of the loop), mirroring _e5_lfsr_bits in e5a.jl.

Notes

  • Found while adding Galileo E5a; deliberately left out of that change to keep it focused.
  • Measured on Julia 1.12, --project, steady-state median of 11 runs (first-call compilation excluded).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions