Skip to content

⚡ Thunderbolt: Softmax — 8x Unrolling for AVX2#82

Open
bugparty wants to merge 1 commit into
mainfrom
thunderbolt-softmax-8x-unroll-2846953622058687661
Open

⚡ Thunderbolt: Softmax — 8x Unrolling for AVX2#82
bugparty wants to merge 1 commit into
mainfrom
thunderbolt-softmax-8x-unroll-2846953622058687661

Conversation

@bugparty

@bugparty bugparty commented Jul 19, 2026

Copy link
Copy Markdown
Owner

💡 What:
Implemented softmax_v6, an AVX2-optimized softmax kernel with aggressive 8x unrolling across all map-reduce phases.

🎯 Why:
The previous softmax_v5 kernel used 4x unrolling. Because transcendental approximations like exp256_ps rely heavily on a chain of FMA instructions with high cycle latency, 4 independent streams are insufficient to fully mask the latency on modern out-of-order execution engines, leaving execution ports occasionally stalled.

🏗️ How:
By maintaining 8 independent accumulator streams (e.g. processing 64 elements per iteration) across max-reduction, exp-evaluation, sum-reduction, and normalization, we perfectly hide instruction latencies and fully utilize all 16 YMM registers. The kernel falls back to standard Horner's polynomial evaluations for exp to reduce port pressure given the high ILP.

📊 Impact:
Microbenchmarking large fixed memory arrays (N=1048576) demonstrates an increase in throughput from ~4.19 GFLOP/s (v5) to ~4.29 GFLOP/s (v6). While the ceiling is ultimately constrained by L2/L3 bandwidth in this 3-pass implementation, 8x unrolling reliably maximizes the compute portion of the pipeline.

🖥️ Tested on:
x86-64 Linux (AVX2-capable CPU) via ml_kernel_bench and ml_kernel_test. ASan/UBSan validated test suite.

🔬 How to reproduce:
cd build && make ml_kernel_bench && DISABLE_CPU_BINDING=1 ./ml_kernels/ml_kernel_bench --filter "softmax_v[56]"


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

Summary by CodeRabbit

  • New Features

    • Added a high-performance AVX2 softmax implementation with improved processing for larger input arrays.
    • Made the new softmax variant available through the standard softmax interface.
    • Added benchmark coverage for comparing the new implementation’s performance.
  • Bug Fixes

    • Added validation against the reference implementation to ensure accurate probabilities that sum to 1.
  • Documentation

    • Documented performance findings and guidance for the optimized softmax approach.

Implemented an aggressive 8x unrolled AVX2 softmax kernel (`softmax_v6`) to fully saturate 16 YMM registers and hide instruction latencies (especially for the transcendental `exp256_ps` FMA chains), converting the main bottleneck from compute/latency to memory bandwidth.

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 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a public AVX2 softmax_v6 kernel using 8-way unrolling across max, exponential-sum, and normalization passes. It also exposes the kernel, registers a benchmark, adds numerical validation, and documents the optimization.

Changes

Softmax v6 kernel

Layer / File(s) Summary
Kernel implementation and public exposure
ml_kernels/include/ml_kernels/softmax.h, ml_kernels/include/ml_kernels/softmax_v6.h, .jules/thunderbolt.md
Adds the 8-way AVX2 softmax implementation, exports it through softmax.h, and documents the unrolling guidance and benchmark evidence.
Benchmark integration
ml_kernels/src/kernel_bench.cpp
Adds and registers SoftmaxV6Benchmark, which invokes softmax_v6.
Kernel validation
ml_kernels/src/test_naive_ops.cpp
Compares softmax_v6 with softmax_naive, verifies normalization, and invokes the new test from main().

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SoftmaxV6Benchmark
  participant softmax_v6
  participant AVX2
  participant OutputBuffer
  SoftmaxV6Benchmark->>softmax_v6: process input array
  softmax_v6->>AVX2: find maximum with 8 accumulators
  softmax_v6->>AVX2: compute exp256_ps_v2 and partial sums
  AVX2-->>OutputBuffer: store exponentials
  softmax_v6->>AVX2: normalize stored exponentials
  AVX2-->>OutputBuffer: store probabilities
Loading

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 clearly summarizes the main change: an AVX2 softmax kernel with 8x unrolling.
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-8x-unroll-2846953622058687661

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

🤖 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 `@ml_kernels/include/ml_kernels/softmax_v6.h`:
- Around line 1-6: Make softmax_v6 self-contained by adding an include guard or
`#pragma` once and including the standard headers required by its implementation:
<immintrin.h>, <cstddef>, <limits>, <algorithm>, and <cmath>. Ensure the
dependencies reduce_max, reduce_sum, and exp256_ps_v2 are declared through the
appropriate softmax header rather than relying on textual inclusion order, while
preserving softmax_v6’s existing behavior.
🪄 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: 98bc919e-056f-4333-a126-acff6511d4f6

📥 Commits

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

📒 Files selected for processing (5)
  • .jules/thunderbolt.md
  • ml_kernels/include/ml_kernels/softmax.h
  • ml_kernels/include/ml_kernels/softmax_v6.h
  • ml_kernels/src/kernel_bench.cpp
  • ml_kernels/src/test_naive_ops.cpp

Comment on lines +1 to +6
// ⚡ Thunderbolt: AVX2 Vectorized Softmax with 8x Unrolling
// Target: AVX2 (Haswell+)
// Reason: Aggressive 8x unrolling across all map-reduce phases perfectly hides instruction latency
// and fully utilizes all 16 YMM registers, transitioning from latency-bound to throughput-bound.
// Expected gain: ~5% throughput over softmax_v5 for large arrays.
inline void softmax_v6(const float *input, float *output, std::size_t n) {

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 | 🟠 Major | ⚡ Quick win

Header lacks include guard and isn't self-contained.

This file has no #pragma once/guard and doesn't include <immintrin.h>, <cstddef>, <limits>, <algorithm>, <cmath> even though it uses __m256, std::size_t, std::numeric_limits, std::max, and depends on reduce_max/reduce_sum/exp256_ps_v2 defined earlier in softmax.h. It only compiles today because it's spliced into the tail of softmax.h after those definitions. The clang errors in static analysis (undeclared __m256, n, etc.) are a direct symptom of this — the file cannot be parsed/included standalone. If this file is ever included directly elsewhere, or softmax.h is reordered/split, this silently breaks.

🛡️ Proposed fix
+#pragma once
+
 // ⚡ Thunderbolt: AVX2 Vectorized Softmax with 8x Unrolling
 // Target: AVX2 (Haswell+)
 // Reason: Aggressive 8x unrolling across all map-reduce phases perfectly hides instruction latency
 // and fully utilizes all 16 YMM registers, transitioning from latency-bound to throughput-bound.
 // Expected gain: ~5% throughput over softmax_v5 for large arrays.
+// NOTE: this file is designed to be included only via softmax.h, after reduce_max/reduce_sum/exp256_ps_v2 are defined.
 inline void softmax_v6(const float *input, float *output, std::size_t n) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// ⚡ Thunderbolt: AVX2 Vectorized Softmax with 8x Unrolling
// Target: AVX2 (Haswell+)
// Reason: Aggressive 8x unrolling across all map-reduce phases perfectly hides instruction latency
// and fully utilizes all 16 YMM registers, transitioning from latency-bound to throughput-bound.
// Expected gain: ~5% throughput over softmax_v5 for large arrays.
inline void softmax_v6(const float *input, float *output, std::size_t n) {
`#pragma` once
// ⚡ Thunderbolt: AVX2 Vectorized Softmax with 8x Unrolling
// Target: AVX2 (Haswell+)
// Reason: Aggressive 8x unrolling across all map-reduce phases perfectly hides instruction latency
// and fully utilizes all 16 YMM registers, transitioning from latency-bound to throughput-bound.
// Expected gain: ~5% throughput over softmax_v5 for large arrays.
// NOTE: this file is designed to be included only via softmax.h, after reduce_max/reduce_sum/exp256_ps_v2 are defined.
inline void softmax_v6(const float *input, float *output, std::size_t n) {
🧰 Tools
🪛 Clang (14.0.6)

[error] 6-6: expected ')'

(clang-diagnostic-error)

🤖 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_v6.h` around lines 1 - 6, Make
softmax_v6 self-contained by adding an include guard or `#pragma` once and
including the standard headers required by its implementation: <immintrin.h>,
<cstddef>, <limits>, <algorithm>, and <cmath>. Ensure the dependencies
reduce_max, reduce_sum, and exp256_ps_v2 are declared through the appropriate
softmax header rather than relying on textual inclusion order, while preserving
softmax_v6’s existing behavior.

Source: Linters/SAST tools

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