Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Decides hand-vs-compiler for loop transforms (unrolling, SIMD, fusion, hoisting). Use when reviewing/authoring a hot loop or tempted to hand-optimize one.
.claude/skills/athola-loop-optimization/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 5% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 38% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 152% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 103% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 44% | 0% |
A decision rule for the five common loop transformations. Its value is knowing when manual application is redundant (the compiler already does it) or harmful (it defeats the vectorizer or fools your benchmark).
body, shift-instead-of-multiply, bespoke SIMD).
optimize it by hand.
Skill(pensive:architecture-review).
Skill(pensive:performance-review).
profiler.
loop-invariant code motion and strength reduction: both run automatically at -O2/-O3, so the manual form is redundant. Leave unrolling to the compiler as well. Unlike the other two it is not on by default (GCC needs -funroll-loops), but the compiler owns the profitability decision and manual unrolling routinely defeats the auto-vectorizer.
restrict /__restrict__) and loop shape first. Confirm with an optimization report (-fopt-info-vec-missed, -Rpass-missed=loop-vectorize). Reach for intrinsics last and accept the portability cost.
compiler misses, loop fusion (guard against register and cache pressure), and multi-accumulator unrolling to break a floating-point reduction chain the compiler legally will not reorder.
vectorize via NumPy, fuse passes via numexpr/Numba. Do not hand-unroll or hand-strength-reduce: the cost is bytecode dispatch, not loop control.
above, and the compiler will not apply it for you. Reach for it only on a profiled hot loop whose branch outcome depends on unpredictable data, and only after checking the production selectivity distribution.
| Technique | Helps where | When NOT to apply by hand | |-----------|-------------|---------------------------| | Unrolling | C/C++/Rust FP reduction chains (multi-accumulator) | Auto-vectorizable loops (defeats vectorizer); OOO CPUs; icache pressure; Python | | SIMD / vectorization | C/C++/Rust loops the compiler misses; Python via NumPy | Before fixing aliasing/loop shape; short trip counts; unverified that emitted SIMD runs | | Loop fusion | Bandwidth-bound array loops; Python via numexpr/Numba | When it spills registers or mixes strided access; compute-bound bodies; blocks vectorization | | Hoisting (LICM) | Python (no compiler does it); C/C++/Rust only when aliasing blocks the proof | -O2+ compiled code: redundant and can lengthen live ranges | | Strength reduction | Compilers do it; near-useless by hand | -O2+ compiled code: blocks the compiler's IV analysis and vectorization | | Branch elimination (branchless) | Hot loops whose branch tracks unpredictable data | Predictable branches; selectivity stably skewed toward one side; sorted or clustered input; before profiling |
A separate axis from the five transforms above. Those change loop structure. This one removes control flow from inside the body. The compiler will not do it for you. Rewriting a conditional push as an unconditional store plus a conditional index advance changes which memory the loop writes, so LLVM cannot apply it as a semantics-preserving transformation.
The lever is branch misprediction, not instruction count. A branch whose outcome tracks unpredictable data costs roughly 15-20 cycles per miss. A predictable branch (loop conditions, bounds checks) is close to free and needs no treatment at all.
Worked example: filtering 1M random f64 values against a threshold on an Intel i7-10875H.
| Selectivity | .filter().collect() | Branchless | |---|---|---| | 1% | 0.59 ms | 1.09 ms | | 25% | 2.69 ms | 1.05 ms | | 50% | 3.94 ms | 1.03 ms | | 75% | 2.75 ms | 1.02 ms | | 99% | 1.49 ms | 1.11 ms |
Read that table as variance, not speed. Branchless does not make the loop faster. It makes the cost independent of the data, winning the 50% worst case by about 4x and losing the 1% best case by about 2x. The same 50% case on sorted input runs at 0.93 ms under the ordinary branchy filter, beating branchless outright, because a sorted predicate predicts perfectly.
The decision therefore turns on the production selectivity distribution, not on any single benchmark row. Apply it when the predicate is near-random and the worst case is what hurts. Skip it when selectivity is stably skewed, or when input arrives sorted or clustered.
Two costs the timing column hides. The output buffer is allocated at full input length, so a 1% filter over 1M f64 reserves 8 MB to return 80 KB. And the branchless form is harder to read, which is a maintenance cost paid on every future edit rather than once.
Source: https://www.greyblake.com/blog/branchless-rust/
or synthetic input can invert to slower on production data, because synthetic input hides effects such as branch misprediction on real value distributions. Benchmark on production-distribution data with optimizer barriers, or do not claim the win.
compiler emitted SIMD" does not mean "SIMD ran." Confirm with codegen or optimization reports, not source inspection.
Both traps tie into Skill(imbue:proof-of-work): a speedup claim needs evidence on representative data, not assertion.
recommendation is "do not optimize."
left to the compiler unless an optimization report shows the compiler failed (aliasing) and the manual form was verified faster.
check that the vectorized path actually executes.
data, not synthetic or reused input.
branch is data-dependent and mispredicting, and reports the selectivity range it was measured across, not a single point.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 23,346 | 14,761 | -37% | 1 | 1 | 0% | 3,717 | 3,892 | +5% | 0 | 0 | — |
case-02 | pass→pass | 13,492 | 7,682 | -43% | 1 | 1 | 0% | 2,103 | 2,899 | +38% | 0 | 0 | — |
case-03 | pass→pass | 6,130 | 4,947 | -19% | 1 | 1 | 0% | 964 | 2,426 | +152% | 0 | 0 | — |
case-04 | pass→pass | 7,305 | 4,259 | -42% | 1 | 1 | 0% | 1,183 | 2,397 | +103% | 0 | 0 | — |
case-05 | pass→pass | 15,842 | 11,690 | -26% | 1 | 1 | 0% | 2,597 | 3,750 | +44% | 0 | 0 | — |
case-06 | pass→pass | 14,955 | 8,523 | -43% | 1 | 1 | 0% | 2,425 | 3,122 | +29% | 0 | 0 | — |
case-07 | pass→pass | 14,403 | 10,214 | -29% | 1 | 1 | 0% | 2,553 | 3,457 | +35% | 0 | 0 | — |
case-08 | pass→pass | 13,752 | 5,749 | -58% | 1 | 1 | 0% | 2,226 | 2,716 | +22% | 0 | 0 | — |
case-09 | pass→pass | 23,458 | 4,088 | -83% | 1 | 1 | 0% | 3,739 | 2,359 | -37% | 0 | 0 | — |
case-10 | pass→pass | 14,962 | 10,712 | -28% | 1 | 1 | 0% | 2,299 | 3,363 | +46% | 0 | 0 | — |
case-11 | pass→pass | 15,232 | 6,133 | -60% | 1 | 1 | 0% | 2,404 | 2,649 | +10% | 0 | 0 | — |
case-12 | pass→pass | 13,486 | 7,734 | -43% | 1 | 1 | 0% | 2,233 | 3,044 | +36% | 0 | 0 | — |
case-13 | pass→pass | 15,508 | 10,979 | -29% | 1 | 1 | 0% | 2,545 | 3,358 | +32% | 0 | 0 | — |
case-14 | pass→pass | 8,585 | 4,964 | -42% | 1 | 1 | 0% | 1,351 | 2,537 | +88% | 0 | 0 | — |
case-15 | pass→pass | 13,745 | 12,995 | -5% | 1 | 1 | 0% | 2,217 | 3,702 | +67% | 0 | 0 | — |
case-16 | pass→pass | 4,596 | 3,998 | -13% | 1 | 1 | 0% | 748 | 2,461 | +229% | 0 | 0 | — |
case-17 | pass→pass | 7,441 | 5,320 | -29% | 1 | 1 | 0% | 1,257 | 2,545 | +102% | 0 | 0 | — |
case-18 | pass→pass | 14,129 | 4,622 | -67% | 1 | 1 | 0% | 2,256 | 2,431 | +8% | 0 | 0 | — |
case-19 | pass→pass | 15,031 | 15,302 | +2% | 1 | 1 | 0% | 2,584 | 4,175 | +62% | 0 | 0 | — |
case-20 | pass→pass | 11,191 | 5,436 | -51% | 1 | 1 | 0% | 1,812 | 2,548 | +41% | 0 | 0 | — |
case-21 | pass→pass | 12,818 | 9,058 | -29% | 1 | 1 | 0% | 2,017 | 3,074 | +52% | 0 | 0 | — |
case-22 | pass→pass | 14,852 | 9,241 | -38% | 1 | 1 | 0% | 2,485 | 3,247 | +31% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.