Finch/examples/simd.fch
Claude 0ceaaa27c4
Floats as written, never fused; fma and muladd; f32/f64 bits, f16 and bf16
- a * b + c is two roundings on every processor and --cpu: the IR has no fast-math flags, and clang
  compiles it with -ffp-contract=on (checked in the assembly: never fused, also with FMA hardware)
- fma(a, b, c) / v.fma(b, c): one rounding, always (llvm.fma)
- muladd(a, b, c) / v.muladd(b, c): the fastest way here (llvm.fmuladd), what v.fma was before
- f32_bits / f32_from_bits / f64_bits / f64_from_bits: bitcasts, also of vectors
- f16_to_f32 / f32_to_f16 (LLVM half), bf16_to_f32 / f32_to_bf16 (integer ops, ties to even, NaN kept)
- docs (en, pl): what is guaranteed about floats, the new built-ins

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Psc42moPLwP43Cmb9oZqSR
2026-11-01 15:25:40 +00:00

69 lines
1.7 KiB
Text

// Vectors (SIMD): eight f32 at once instead of one.
//
// finch run examples/simd.fch runs on any x86-64 or ARM64 processor
// finch run examples/simd.fch --cpu native uses all of this one's instructions (AVX2, AVX-512, NEON)
import time
// one number at a time
fn dot_plain([]f32 a, []f32 b) -> f32 {
f32 total = 0.0
for i in 0..a.len {
total += a[i] * b[i]
}
return total
}
// eight at a time: f32x8(list, i) takes list[i] ... list[i+7]; muladd multiplies and adds, in one step where it can
fn dot_simd([]f32 a, []f32 b) -> f32 {
acc := f32x8(0.0)
i := 0
while i + 8 <= a.len {
acc = f32x8(a, i).muladd(f32x8(b, i), acc)
i += 8
}
total := acc.sum()
while i < a.len { // the few that are left
total += a[i] * b[i]
i += 1
}
return total
}
// brighten every color: the same work on every lane, with min keeping it below 1
fn brighten([]f32 pixels, f32 amount) {
i := 0
while i + 8 <= pixels.len {
v := min(f32x8(pixels, i) * (1.0 + amount), 1.0)
v.store(pixels, i)
i += 8
}
}
fn main() {
[]f32 a
[]f32 b
for i in 0..4_000_000 {
a.push(f32(i % 100) / 100.0)
b.push(0.5)
}
start := time.millis()
x := 0.0
for round in 0..20 {
x = float(dot_plain(a, b))
}
plain := time.millis() - start
start = time.millis()
y := 0.0
for round in 0..20 {
y = float(dot_simd(a, b))
}
simd := time.millis() - start
print("one at a time: ", str(x, 1), "in", plain, "ms")
print("eight at a time:", str(y, 1), "in", simd, "ms")
brighten(a, 0.5)
print("brightened:", a[10], a[90], a.len)
}