Skip to content

⚡ Thunderbolt: Softmax — 8x unroll via single-FMA exp256 optimization#68

Open
bugparty wants to merge 1 commit into
mainfrom
thunderbolt-softmax-v6-11879846324715467560
Open

⚡ Thunderbolt: Softmax — 8x unroll via single-FMA exp256 optimization#68
bugparty wants to merge 1 commit into
mainfrom
thunderbolt-softmax-v6-11879846324715467560

Conversation

@bugparty

@bugparty bugparty commented Jul 1, 2026

Copy link
Copy Markdown
Owner

💡 What: Added softmax_v6 optimized for AVX2, which unrolls the main computation loop 8x to process 64 elements at once. To avoid register spilling during this heavy unroll, the transcendental approximation exp256_ps_v3 was modified to use a single FMA for r = x - n * ln(2) by combining constants.

🎯 Why: The previous softmax_v5 implementation was bottlenecked by FMA instruction latency and register pressure. Reducing the instructions required for the exp approximation frees up enough registers to allow aggressive 8x unrolling, fully saturating execution ports and hiding latency.

🏗️ How:

  • Modified exp256_ps to compute r = _mm256_fnmadd_ps(n, ln2_constant, x).
  • Unrolled the max, exp/sum, and normalize loops in softmax_v6 by 8 to maintain 8 independent accumulators.
  • Ensured correctness is verified by adding a 72-element correctness test to test_naive_ops.cpp.

📊 Impact:
On N=1,048,576 (Fixed Memory mode):

  • softmax_v5: 4.17 GFLOP/s
  • softmax_v6: 4.57 GFLOP/s (~9.6% speedup)

🖥️ Tested on: Intel Xeon @ 2.30GHz (AVX2-capable), Ubuntu Linux, GCC 13.3.0.

🔬 How to reproduce:

cd build
make -j$(nproc) ml_kernel_bench
DISABLE_CPU_BINDING=1 ./ml_kernels/ml_kernel_bench --filter "softmax_v6"

PR created automatically by Jules for task 11879846324715467560 started by @bugparty

Summary by CodeRabbit

  • New Features

    • Added a faster softmax option for supported processors, with improved performance on larger inputs.
    • Introduced a new optimized exponential calculation path to support the updated softmax behavior.
    • Added benchmark coverage for the new softmax implementation.
  • Tests

    • Expanded automated checks to validate accuracy and normalization on larger input sizes.

Co-authored-by: bugparty <1510776+bugparty@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new AVX2+FMA exponential helper exp256_ps_v3 and softmax kernel softmax_v6 with 8x loop unrolling in softmax.h, registers a SoftmaxV6Benchmark, adds a test_softmax_v6 correctness test, and documents the optimization in project notes.

Changes

Softmax v6 kernel and validation

Layer / File(s) Summary
exp256_ps_v3 and softmax_v6 implementation
ml_kernels/include/ml_kernels/softmax.h
Adds a single-FMA range-reduction exponential helper and a numerically-stable softmax kernel using 8-way unrolled 64-element blocks for max, sum, and normalization phases.
Benchmark and correctness test
ml_kernels/src/kernel_bench.cpp, ml_kernels/src/test_naive_ops.cpp
Registers SoftmaxV6Benchmark dispatching to softmax_v6 and adds test_softmax_v6 verifying output matches softmax_naive and sums to 1.0.
Optimization notes documentation
.jules/thunderbolt.md
Documents the single-FMA exp256 optimization rationale and evidence.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately highlights the main change: an 8x-unrolled softmax with a single-FMA exp256 optimization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch thunderbolt-softmax-v6-11879846324715467560

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
ml_kernels/src/kernel_bench.cpp (1)

337-342: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the new benchmark methods on brace-next-line style.

name() and run() use inline function-body braces on the same line. Please switch them to brace-next-line form to match the repository's C/C++ style. As per coding guidelines, Keep braces on their own lines for function bodies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ml_kernels/src/kernel_bench.cpp` around lines 337 - 342, The new benchmark
methods in the softmax_v6 benchmark class use same-line function-body braces,
which violates the repository’s brace-next-line style. Update the inline
overrides for name() and run() in kernel_bench.cpp so their opening and closing
braces are on their own lines, matching the existing C/C++ formatting used by
the benchmark classes.

Source: Coding guidelines

ml_kernels/src/test_naive_ops.cpp (2)

183-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the new test functions on brace-next-line style.

test_softmax_v6() and main() keep the opening brace on the signature line. Please move it to its own line for consistency with the repository's C/C++ style. As per coding guidelines, Keep braces on their own lines for function bodies.

Also applies to: 216-216

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ml_kernels/src/test_naive_ops.cpp` at line 183, The new test functions are
using the opening brace on the signature line instead of the repository’s
brace-next-line style. Update the function definitions for test_softmax_v6() and
main() so their opening braces are placed on their own lines, matching the
existing C/C++ formatting convention used in this test file.

Source: Coding guidelines


185-210: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Exercise the scalar tail too.

This 72-element case only hits the 64-wide unrolled body plus one 8-float vector remainder. The scalar tail in softmax_v6 never executes, so regressions in that path would still slip past this test. Add a second case with a non-multiple-of-8 size such as 69 or 71.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ml_kernels/src/test_naive_ops.cpp` around lines 185 - 210, The softmax
comparison test only covers the 64-wide unrolled path plus an 8-float vector
remainder, so the scalar tail in softmax_v6 is still untested. Update the
existing test in test_naive_ops.cpp by adding a second input case with a size
that is not a multiple of 8, using softmax_naive and softmax_v6 again to compare
outputs and verify the sum; keep the new case alongside the current softmax_v6
test so both the vector remainder and scalar tail paths are exercised.
ml_kernels/include/ml_kernels/softmax.h (1)

505-505: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Match the repository brace style.

Both new function definitions keep the opening brace on the signature line. Move it to its own line so this header stays consistent with the C/C++ style used elsewhere in changed code. As per coding guidelines, Keep braces on their own lines for function bodies.

Also applies to: 541-541

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ml_kernels/include/ml_kernels/softmax.h` at line 505, The new function
definitions in softmax.h use a brace style that differs from the repository
convention: move the opening brace for exp256_ps_v3 and the other affected AVX
function to its own line. Keep the function signatures unchanged, and update the
body formatting so these definitions match the existing C/C++ brace style used
throughout the header.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.jules/thunderbolt.md:
- Around line 31-34: The entry under the 2024-05-24 heading in the thunderbolt
note is out of chronological order relative to the surrounding 2024-10-24 to
2024-10-26 entries. Update the note by either correcting the date if it is a
typo, or moving this "Single-FMA exp256 optimization" section to the proper
chronological position so the timeline reads forward; use the date heading and
the entry text as the unique anchors.

---

Nitpick comments:
In `@ml_kernels/include/ml_kernels/softmax.h`:
- Line 505: The new function definitions in softmax.h use a brace style that
differs from the repository convention: move the opening brace for exp256_ps_v3
and the other affected AVX function to its own line. Keep the function
signatures unchanged, and update the body formatting so these definitions match
the existing C/C++ brace style used throughout the header.

In `@ml_kernels/src/kernel_bench.cpp`:
- Around line 337-342: The new benchmark methods in the softmax_v6 benchmark
class use same-line function-body braces, which violates the repository’s
brace-next-line style. Update the inline overrides for name() and run() in
kernel_bench.cpp so their opening and closing braces are on their own lines,
matching the existing C/C++ formatting used by the benchmark classes.

In `@ml_kernels/src/test_naive_ops.cpp`:
- Line 183: The new test functions are using the opening brace on the signature
line instead of the repository’s brace-next-line style. Update the function
definitions for test_softmax_v6() and main() so their opening braces are placed
on their own lines, matching the existing C/C++ formatting convention used in
this test file.
- Around line 185-210: The softmax comparison test only covers the 64-wide
unrolled path plus an 8-float vector remainder, so the scalar tail in softmax_v6
is still untested. Update the existing test in test_naive_ops.cpp by adding a
second input case with a size that is not a multiple of 8, using softmax_naive
and softmax_v6 again to compare outputs and verify the sum; keep the new case
alongside the current softmax_v6 test so both the vector remainder and scalar
tail paths are exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9787adea-c29f-4539-b1e9-ab34c6707c5c

📥 Commits

Reviewing files that changed from the base of the PR and between acca01e and 90230a9.

⛔ Files ignored due to path filters (1)
  • v6.log is excluded by !**/*.log
📒 Files selected for processing (4)
  • .jules/thunderbolt.md
  • ml_kernels/include/ml_kernels/softmax.h
  • ml_kernels/src/kernel_bench.cpp
  • ml_kernels/src/test_naive_ops.cpp

Comment thread .jules/thunderbolt.md
Comment on lines +31 to +34
## 2024-05-24 - Single-FMA exp256 optimization
**Learning:** In transcendental AVX2 SIMD approximations like exp256, combining constants for `r = x - n * ln(2)` into a single FMA instruction (rather than splitting `ln(2)` for exact precision) reduces register pressure and instruction count. This unlocks the ability to aggressively unroll the softmax loop 8x (processing 64 elements at once) without spilling registers, perfectly hiding latency and saturating the L1 cache bandwidth, all while staying within acceptable numerical ML tolerances.
**Evidence:** Implementation of `softmax_v6` showing significant reduction in instruction count and higher throughput compared to `softmax_v5`.
**Action:** When working on approximations where exact precision isn't strictly necessary, look for opportunities to combine mathematical constants to save registers and instructions, allowing for higher unrolling factors.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the note's date/order.

This entry is dated 2024-05-24 but is appended after the 2024-10-24 to 2024-10-26 entries, so the chronology now reads backward. If the date is intentional, move the section; otherwise fix the typo.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.jules/thunderbolt.md around lines 31 - 34, The entry under the 2024-05-24
heading in the thunderbolt note is out of chronological order relative to the
surrounding 2024-10-24 to 2024-10-26 entries. Update the note by either
correcting the date if it is a typo, or moving this "Single-FMA exp256
optimization" section to the proper chronological position so the timeline reads
forward; use the date heading and the entry text as the unique anchors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant