portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum - #20090

Merged
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16
Jun 17, 2026
Merged

portable: accumulate in fp32 for Half/BFloat16 in softmax, log_softmax, mean, and sum#20090
GregoryComer merged 3 commits into
pytorch:mainfrom
vacu9708:fp32-accumulation-bfloat16

Conversation

@vacu9708

@vacu9708vacu9708 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

This PR follows up on #19117 (op_grid_sampler_2d)

Motivation

softmax, log_softmax, mean, and sum all accumulate their reduction in the input dtype. For BFloat16, that sum saturates around 256. Once it gets there, adding 1.0 rounds away and the total gets stuck. A uniform softmax over 512 elements in BFloat16 gives ~1/256 per output instead of 1/512.

Why FP32 accumulation is needed

BFloat16 has the same exponent width as Float32, so it has a similar range. However, it has far fewer fraction bits, which makes its representable spacing much coarser as values grow.

TypeExponent bitsFraction bitsPractical effect
BFloat1687Similar range to Float32, but coarse spacing
Float32823Similar range, much finer spacing

For BFloat16, the gap between consecutive representable values (i.e, the smallest step size) increases at each power-of-two range:

RangeBFloat16 step sizeRepresentable examples
[128, 256)1128, 129, 130, ..., 255
[256, 512)2256, 258, 260, ..., 510

As a result, once a BFloat16 running sum reaches 256, adding 1.0 no longer changes the value:

OperationExact resultBFloat16 resultReason
256 + 1257256257 is not representable and rounds back to 256 (according to IEEE 754; round-to-nearest-even)

This directly affects all four ops for large inputs. For a softmax over 512 zeros, each exp(0) contributes 1.0, so the denominator should be 512. If the BFloat16 accumulation gets stuck at 256, the output becomes approximately 1/256 instead of the correct 1/512.

CaseExpected denominatorBFloat16 accumulated denominatorOutput
Correct accumulation5125121/512
BFloat16 accumulation512~256~1/256

Tests

$ cmake --build cmake-out --target portable_kernels_test -j$(nproc)
[100%] Built target portable_kernels_test
# Post-fix — new tests:
[ OK ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ OK ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
# Pre-fix (reverted op files):
[ FAILED ] OpSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpMeanOutTest.BFloat16LargeDimAccumulatesInFloat
[ FAILED ] OpSumOutTest.BFloat16LargeDimAccumulatesInFloat
$ lintrunner op_softmax.cpp op_log_softmax.cpp op_mean.cpp op_sum.cpp \
op_softmax_test.cpp op_log_softmax_test.cpp op_mean_test.cpp op_sum_test.cpp
ok No lint issues.

cc @larryliu0820@manuelcandales

@pytorch-bot

pytorch-botBot commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20090

Note: Links to docs will display an error until the docs builds have been completed.

❌ 3 New Failures

As of commit cbbb3dc with merge base 71cbe9f (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-clameta-claBot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 8, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch 2 times, most recently from 3fb0012 to d56aa5aCompareJune 8, 2026 06:02
@nil-is-allnil-is-all added the module: kernels Issues related to kernel libraries and utilities, and code under kernels/ label Jun 15, 2026

@GregoryComerGregoryComer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, the changes look good. Thanks for fixing this.

There is a failure in CI that looks legitimate. Could you take a look? This is from the unit test job.

2026-06-15T20:57:20.7535620Z [ RUN ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat
2026-06-15T20:57:20.7535990Z E 00:00:00.161654 executorch:op_log_softmax.cpp:152] Check failed (false): 2026-06-15T20:57:20.7536490Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/kernels/test/op_log_softmax_test.cpp:382: Failure
2026-06-15T20:57:20.7536960Z Value of: (out)
2026-06-15T20:57:20.7543010Z Expected: is close to with tol (ETensor(sizes={1, 512}, dtype=BFloat16, data={-6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25, -6.25}), 1e-05, 0.1)
2026-06-15T20:57:20.7551240Z Actual: ETensor(sizes={1, 512}, dtype=BFloat16, data={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) (of type executorch::runtime::etensor::Tensor)
2026-06-15T20:57:20.7553800Z /Users/ec2-user/runner/_work/executorch/executorch/pytorch/executorch/../executorch/kernels/test/TestUtil.h:108: Failure
2026-06-15T20:57:20.7554230Z Expected equality of these values:
2026-06-15T20:57:20.7554440Z context_.failure_state()
2026-06-15T20:57:20.7554620Z Which is: 4-byte object <12-00 00-00>
2026-06-15T20:57:20.7554830Z torch::executor::Error::Ok
2026-06-15T20:57:20.7555010Z Which is: 4-byte object <00-00 00-00>
2026-06-15T20:57:20.7555320Z [ FAILED ] OpLogSoftmaxOutTest.BFloat16LargeDimAccumulatesInFloat (0 ms)

…tmax
Problem:
Softmax and log_softmax accumulated exp(x - max) in the tensor dtype.
For BFloat16, the running sum saturates around 256 — adding 1.0 stops
changing the total — so a uniform softmax over N=512 elements outputs
~1/256 instead of 1/512.
Changes:
Accumulate the exp-sum in float for Half/BFloat16 by threading an ACC
type through the map-reduce calls. Loads and stores remain in the tensor
dtype.
Continues the fp32-accumulation work in pytorch#19117.
Problem:
The fast-path and generic reduction loops in mean.out and sum.IntList_out
accumulated the running sum in the tensor dtype. For BFloat16, the sum
saturates around 256, so a mean over N=512 all-ones elements gives 0.5
instead of 1.0, and summing 512 all-ones elements gives 256 instead of
512.
Changes:
Accumulate in float for Half/BFloat16 by promoting the loop accumulator
to ACC in both the fast path and the generic path. The final result is
cast back to the tensor dtype on store.
Continues the fp32-accumulation work in pytorch#19117.
opt_log_softmax_out only handled Float; BFloat16 and Half fell through to
ET_KERNEL_CHECK(false), leaving output unchanged. The underlying
log_softmax_kernel<IN_T, OUT_T> is fully generic and the ATen vectorized
functions it delegates to already support BFloat16 and Half.
- Extend log_softmax_wrapper with an if constexpr branch for BFloat16/Half
that calls log_softmax_kernel<T, T>
- Add BFloat16 and Half dispatch cases in opt_log_softmax_out
@vacu9708
vacu9708force-pushed the fp32-accumulation-bfloat16 branch from d56aa5a to cbbb3dcCompareJune 16, 2026 03:44
@vacu9708

vacu9708 commented Jun 16, 2026

Copy link
Copy Markdown
ContributorAuthor

Thanks for flagging this @GregoryComer.
The failure was in optimized_kernels_test, not portable_kernels_test
op_log_softmax_test.cpp is registered for the optimized variant too, so my
BFloat16 test ran against opt_log_softmax_out, which only handled Float and
fell through to ET_KERNEL_CHECK(context, false, InvalidArgument, out).

I didn't hit this locally because my local build doesn't enable
EXECUTORCH_BUILD_KERNELS_OPTIMIZED. I've since rebuilt with it and confirmed
op_log_softmax_test passes against both kernel variants.

Fixed by adding BFloat16 and Half support to opt_log_softmax_out.

@vacu9708

vacu9708 commented Jun 17, 2026

Copy link
Copy Markdown
ContributorAuthor

I think all three failures are infra issues:

  • Cadence hifi4 / vision (Input required and not supplied: aws-region):
    • fails in the AWS-credentials step before any build.
  • test-models-linux-basic (mv3, portable):
    • died during pip install . setup with No matching distribution found for scikit-learn==1.7.1 (from versions: none)

@GregoryComer
GregoryComer merged commit 8bb71cf into pytorch:mainJun 17, 2026
178 of 181 checks passed
@GregoryComer

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

@Gasoonjia

Gasoonjia commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

heads up this PR breaks ExecuTorch's lint rule: https://hud.pytorch.org/hud/pytorch/executorch/main/1?per_page=50&name_filter=lint

you may want to have another PR to solve it, or i have to revert the current PR 20090.

@vacu9708

@vacu9708

Copy link
Copy Markdown
ContributorAuthor

@Gasoonjia I am looking at it and found the root cause. I'll reply shortly.

@vacu9708

vacu9708 commented Jun 18, 2026

Copy link
Copy Markdown
ContributorAuthor

e93a285ebd "Extend CPPCHECK scope to portable kernels" (2026-06-04)
My work branch was cut before this new lint coverage.

I submitted a PR #20368 that fixes this issue. Please review it.

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

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.module: kernelsIssues related to kernel libraries and utilities, and code under kernels/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vacu9708@GregoryComer@Gasoonjia@nil-is-all