# Optimization: Branchless w() (negative result) - **Author**: Linus - **Predecessor**: variant_baseline.cpp - **File(s)**: variants/variant_branchless.cpp, variants/variant_branchless_sqrtfree.cpp - **Status**: probably: negative (slower) ## What this changes Two attempts at making the field-of-view weight function `w()` branchless. **Attempt 1** (`variant_branchless.cpp`): replace the if/else in `w()` with an arithmetic blend `c_neg_times_dt + cond * (dt_neg - c_neg_times_dt)`, where `cond` is a 0.0/1.0 mask cast from a bool comparison. The `sqrt` inside `f.norm()` is kept. **Attempt 2** (`variant_branchless_sqrtfree.cpp`): on top of attempt 1, also eliminate the `sqrt` by squaring the comparison. Uses the fact that `cos_phi = cos(100 deg)` is negative, so `f.norm() * cos_phi <= 0`, meaning the condition is automatically true whenever `e.dot(f) >= 0`. For `e.dot(f) < 0`, both sides are non-positive so we can square them safely: `dot^2 <= f.dot(f) * cos_phi^2`. Needs the constant `cos_phi_sq = cos_phi * cos_phi` in `headers/constants.hpp`. Additionaly i split the mainloop into two loops, so we where able to remove the if-statement from the loop. Once again that didn't made a difference in the runtime analysis ## Code (relevant snippet) Attempt 1 - arithmetic blend (still has sqrt): ```cpp double w(Vector2d e, Vector2d f) { double dot = e.dot(f); double f_norm = f.norm(); double cond = (double)(dot >= f_norm * cos_phi); // 1.0 or 0.0 return c_neg_times_dt + cond * (dt_neg - c_neg_times_dt); } ``` Attempt 2 - blend + sqrt-free (squared comparison): ```cpp double w(Vector2d e, Vector2d f) { double dot = e.x * f.x + e.y * f.y; double f_dot_f = f.x * f.x + f.y * f.y; // Bitwise | (not ||) keeps both sides evaluated -> no branch. double cond = (dot >= 0.0) | (dot * dot <= f_dot_f * cos_phi_sq); return c_neg_times_dt + cond * (dt_neg - c_neg_times_dt); } ``` ## Benchmark Conditions: min of runs, N_ITER=1000, -O3 -march=native -flto. A preliminary run on my laptop (Intel ULTRA 9 185H @ 2.2 GHz, WSL Ubuntu) showed both attempts about 8-13% slower than the baseline across N=50 to N=1000, with a roughly flat gap that did not scale with N. For the official numbers run on the testmachines ### x86 **Machine**: ``` ``` **Frequency**: Attempt 1 - branchless (arithmetic blend): | N | W(n) [Flops] | T(n) [Cycles] | Performance [Flops/Cycle] | Diff vs Baseline | | ---- | ------------ | ------------- | ------------------------- | ---------------- | | 50 | | | | | | 100 | | | | | | 200 | | | | | | 500 | | | | | | 1000 | | | | | Attempt 2 - branchless + sqrt-free: | N | W(n) [Flops] | T(n) [Cycles] | Performance [Flops/Cycle] | Diff vs Baseline | | ---- | ------------ | ------------- | ------------------------- | ---------------- | | 50 | | | | | | 100 | | | | | | 200 | | | | | | 500 | | | | | | 1000 | | | | | ### ARM (Apple) **Machine**: ``` ``` **Frequency**: Attempt 1 - branchless (arithmetic blend): | N | W(n) [Flops] | T(n) [Cycles] | Performance [Flops/Cycle] | Diff vs Baseline | | ---- | ------------ | ------------- | ------------------------- | ---------------- | | 50 | | | | | | 100 | | | | | | 200 | | | | | | 500 | | | | | | 1000 | | | | | Attempt 2 - branchless + sqrt-free: | N | W(n) [Flops] | T(n) [Cycles] | Performance [Flops/Cycle] | Diff vs Baseline | | ---- | ------------ | ------------- | ------------------------- | ---------------- | | 50 | | | | | | 100 | | | | | | 200 | | | | | | 500 | | | | | | 1000 | | | | | ## Analysis ## Takeaway