76 lines
2.5 KiB
Markdown
76 lines
2.5 KiB
Markdown
# Optimization: SIMD polynomial exp (float, 8-wide) + FTZ/DAZ
|
|
|
|
- **Author**: Linus
|
|
- **Predecessor**: variant_float_inlining_SoA_AVX2.cpp
|
|
- **File(s)**: variants/variant_float_inlining_SoA_AVX2_polyexp.cpp, main-float.cpp (FTZ/DAZ enable)
|
|
- **Status**: TBD, positive locally (approx twice as fast)
|
|
|
|
## What this changes
|
|
|
|
The 8 scalar `expf` calls in the j-loop become a single SIMD polynomial
|
|
approximation `exp256_ps` that operates on `__m256`. Range-reduce
|
|
`x = n*ln2 + r` with `n = round(x/ln2)`, evaluate a degree-5 Horner
|
|
polynomial for `exp(r)` on `r in [-ln2/2, ln2/2]`, then scale by `2^n`
|
|
via the Schraudolph bit-trick on the IEEE 754 exponent slot (shift the
|
|
biased exponent by 23). Degree-5 is enough for float, the polynomial is
|
|
good to ~1e-5 absolute on the reduced range, well under the 1cm verify
|
|
tolerance.
|
|
|
|
|
|
`main-float.cpp` also gets two MXCSR writes at the top of `main()`:
|
|
|
|
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
|
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
|
|
|
This was the part that actually mattered, see Analysis.
|
|
|
|
## Code (relevant snippet)
|
|
|
|
|
|
See `variants/variant_float_inlining_SoA_AVX2_polyexp.cpp` for the full
|
|
j-loop and `main-float.cpp` for the FTZ/DAZ setup.
|
|
|
|
## Benchmark
|
|
|
|
Conditions: min of N runs (TBD), N_ITER=1000, -O3 -march=native -flto.
|
|
Diff is vs `variant_float_inlining_SoA_AVX2`.
|
|
|
|
### x86
|
|
|
|
**Machine**: TBD
|
|
|
|
**Frequency**: TBD
|
|
|
|
| N | W(n) [Flops] | T(n) [Cycles] | Performance [Flops/Cycle] | Diff vs Predecessor |
|
|
| ---- | ------------ | ------------- | ------------------------- | ------------------- |
|
|
| 50 | | | | |
|
|
| 100 | | | | |
|
|
| 200 | | | | |
|
|
| 500 | | | | |
|
|
|
|
### ARM (Apple)
|
|
|
|
Not applicable, AVX2-only. A NEON port would need its own vectorised
|
|
exp.
|
|
|
|
## Analysis
|
|
|
|
roughly twice as fast
|
|
|
|
|
|
## Takeaway
|
|
|
|
Polyexp helps as expected. The real fix is FTZ/DAZ in `main-float.cpp`,
|
|
without it everything downstream in the float chain is silently
|
|
subnormal-stalled
|
|
|
|
## References
|
|
|
|
- Schraudolph, N. N. (1999). "A Fast, Compact Approximation of the
|
|
Exponential Function." Neural Computation 11(4), 853-862.
|
|
- IEEE 754 double-precision format. Wikipedia.
|
|
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
|
|
- Horner's method. Wikipedia.
|
|
https://en.wikipedia.org/wiki/Horner%27s_method
|
|
|