Embedded signal processing
Embedded 8×8 DCT
A fixed-point Loeffler 8×8 discrete cosine transform optimized for an ARM Cortex-A15 target.
Overview
The project implements Loeffler’s separable 8×8 DCT in C for an ARMv7-A Cortex-A15 target. It replaces floating-point work with Q17 fixed-point arithmetic and evaluates each optimization with the same nanosecond-per-call benchmark.
A two-dimensional image path applies the one-dimensional transform across rows and columns, with a 320 × 320 input used to check the complete block-processing flow.
Problem and context
An embedded DCT must reduce compute time without changing the transform structure or relying on floating-point hardware. Optimizations also have to account for the cost of moving values into SIMD lanes, not only the number of arithmetic instructions.
The work therefore compared a sequence of explicit variants rather than presenting one final implementation without a baseline.
Technical approach
The baseline routine was progressively transformed through function inlining, macros, local register use, and several NEON arrangements. Each version remained available in the benchmark so its contribution could be measured independently.
NEON was retained where four parallel first-stage operations mapped cleanly to vector lanes. Later stages remained scalar because lane shuffles and memory traffic outweighed their arithmetic benefit.
Key engineering details
Fixed-point representation
Q17 constants replace floating-point coefficients while preserving sufficient intermediate precision for the integer transform.
Stepwise variants
Routine, Inline, Macro, Register, NeonS1, NeonReflect, NeonFull, and Optimized builds make performance changes directly comparable.
Selective SIMD
The best implementation uses NEON in stage one but avoids full vectorization where rearrangement overhead makes the measured result slower.
Repeatable benchmark
The executable emits CSV timing data per variant and also includes the full two-dimensional image transform path.
Results and outcomes
The optimized implementation averaged 81.42 ns per call versus 373.60 ns for the baseline routine.
That is a measured 78.2% reduction in execution time. The full-NEON variant was slower than the selective implementation, demonstrating the cost of lane movement and memory traffic.