Repository navigation
Convert to BFloat16 with the hardware instruction on the emulated path - #1122
Merged
Merged
Conversation
Without LLVM `bfloat` arithmetic (before Julia 1.13), BFloat16s converts
from `Float32` by rounding the bits as an integer, selecting the NaN
pattern with `isnan(x) ? 0x7fc0 : (bits + bias) >> 16`. Apple's back-end
drops the shift of that select when its result is used as 32 bits, so
shuffling a `BFloat16` (which KernelInterface does as a `UInt32`) or a
`Complex{BFloat16}` returned garbage: e.g. `sum` of a `BFloat16` array is
NaN with GPUArrays' ordered reductions.
Emit the `fptrunc` to `bfloat` that the native path uses instead, which
every supported macOS has.
Contributor
Benchmark resultsCompared with 0877ad5a3, the merge base. Times are the minimum time of every benchmark. A change is unusual when it exceeds both three times the benchmark's typical change between consecutive runs on the default branch and a minimum per category (performance 3%, latency 5%, applications 3%, other 3%). A benchmark that starts or stops allocating is listed however few the allocations. A single run can still be a fluke, so rerun before drawing conclusions.
All benchmarks
History: https://juliagpu.org/perf/?pkg=metal |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before Julia 1.13 there is no LLVM
bfloatarithmetic on Apple silicon, and BFloat16s converts fromFloat32by rounding the bits as an integer:isnan(x) ? 0x7fc0 : (bits + bias) >> 16. Apple's back-end drops the shift of that select when its result is used as 32 bits. The IR and AIR Metal.jl emits are correct. Minimal case, without BFloat16 (macOS 27, M1):Only an
fcmp ord/unocondition combined with a logical shift right by 16 triggers it. Normally the result is truncated toi16right away and nothing goes wrong, but anything that widens the bits gets them wrong:sumandprodof aBFloat16array returnNaNwith GPUArraysmain. Since Reduce with operators not known to commute in element order GPUArrays.jl#822 these reductions use AcceleratedKernels' ordered kernel, whose block scan shuffles the partial results. Metal has no shuffle forBFloat16, so KernelInterface's fallback shuffles the bits as aUInt32. This is the Metal 1.10 failure on GPUArraysmain, Inline the range indexing kernel bodies and launch getindex linearly GPUArrays.jl#839 and Define neutral elements for the @fastmath reduction operators GPUArrays.jl#840.sumof aComplex{BFloat16}array is garbage already with the released GPUArrays (the shuffle fallback packs both fields into one word).map(x -> reinterpret(UInt16, x + x) % UInt32, a)returns the wrong bits.On the emulated path, the device conversion now emits the
fptrunc float to bfloatthat the native path uses. Metal.jl requires macOS 15, so the conversion is available on every supported system. The conversion matched BFloat16s bit for bit on 2M inputs, including subnormals, infinities, signed zeros and rounding ties. The only difference is that NaN payloads are kept rather than normalized to0x7fc0, as on the native path.