1
0
Fork
You've already forked fvml
0
An f64 SIMD vector fast math·s library for Zig
  • Zig 100%
2026年06月25日 17:18:54 +09:00
src Fixed minor compilation errors 2 2026年06月25日 17:18:54 +09:00
.gitignore Initialized repo with fully formed code, who needs git during development? 2026年06月03日 17:54:16 +09:00
build.zig Removed redundant code and simplified repo structure. Enabled operations on arbitrary comptime-known vector lengths. 2026年06月07日 17:08:39 +09:00
build.zig.zon Initialized repo with fully formed code, who needs git during development? 2026年06月03日 17:54:16 +09:00
LICENSE Initial commit 2026年06月03日 10:48:13 +02:00
README.md Added some raw timings for benchmarked functions 2026年06月08日 00:50:47 +09:00

fvml

f64 vector math·s library

A decently fast f64 SIMD vector math library for Zig.

NOTE: This is not a linear algebra vector library. It is a library for SIMD operations.

Features:

  • SIMD-friendly implementations of functions implemented for scalars but not for SIMD vectors in the Zig standard library.
  • Fast approximations for slow existing SIMD functions (e.g. exp, log, mod).
  • A guaranteed relative error under 1e-10 for all functions implemented in the Zig standard library (see Parity with Zig standard library)
  • N-Ary operators (sums, products, means)
  • Extra functions for statistics, biology, and AI/ML.
  • Out-of-the-box support for 256-bit (AVX/AVX2), 512-bit (AVX-512) vectors as well as automatically detected width vectors on supported architectures.
  • Straightforward support for arbitrary-length vectors, as long as the length is known at compile-time.

Getting started

Adding fvml to a project

fvml can be added to any Zig project's build.zig.zon via:

zig fetch --save=fvml git+https://codeberg.org/13091101/fvml

Using fvml

build.zig (an example, modify as you see fit)
conststd=@import("std");pubfnbuild(b:*std.Build)void{consttarget=b.standardTargetOptions(.{});constoptimize=b.standardOptimizeOption(.{});...// Normal build.zig contentsconstfvml_dep=b.dependency("fvml",.{.target=target,.optimize=optimize,});constmod=b.addModule(.{.root_source_file=b.path("src/main.zig"),.optimize=optimize,.target=target,});mod.addImport("fvml",fvml_dep.module("fvml"));constexe=b.addExecutable(.{.name="project-name",.root_module=mod,});b.installArtifact(exe);...// Normal build.zig contents}
main.zig
constfvml=@import("fvml");// AVX-2, AVX-512 can be accessed directlyconstf64x4:type=fvml.f64x4;constf64x8:type=fvml.f64x8;constavx2=fvml.avx2;constavx512=fvml.avx512;// ... Or by their namespaceconstf64x4_separated:type=avx2.f64xN;// Automatic detection of supported vectors is possible on supported hardwareconstautodetected=fvml.auto;constVecN:type=autodetected.f64xN;constvec_len:usize=fvml.auto_vec_len;// Custom vector lengths are also accessible, as they are an alias to Zig's inbuilt `@Vector(type, len)`constcustom=fvml.common.GenerateOps(3);constVec3:type=custom.f64xN;// Operations & SIMD constants are accessible from their namespaceconstcbrt_sin_3:f64x4=avx2.cbrt(avx2.sin(avx2.One+avx2.Two));constsomething_else:f64x8=avx512.sigmoid(avx512.erf(avx512.Pi));// `f64xN` is a type alias, so normal SIMD vector operations workconstthree:f64x4=@splat(3.0);

List of non-Zig-stdlib functions

Unary operators
Binary operators
  • leakyrelu: Leaky ReLU variant of the ReLU function
  • elu: Exponential linear units (ELU)
  • geometric_arithmetic_mean: Arithmetic-geometric mean
  • and, or: Logical operators checking whether values are not zero.
  • bitand, bitor, bitxor: Bitwise operations operating directly on f64. Usage not recommended.
N-ary operators
  • sum: Sum / summation operation. Has an ILP variant.
  • product: Repeated product operation. Has an ILP variant.
  • arithmetic_mean: Arithmetic mean. Has an ILP variant.
  • geometric_mean_unstable: Naive geometric mean implementation that may be numerically unstable for large or small inputs. Has an ILP variant.
  • geometric_mean: Numerically stable geometric mean using a log2/exp2 identity. Slower than the unstable variant (4x in personal benchmarks).
  • harmonic_mean: Harmonic mean operation.
  • hypot: Euclidean / L2 Norm operation. Has an ILP variant.

Parity with Zig standard library

We tested the relative error against the Zig standard library implementation.

  • Tests are run in Debug, ReleaseSafe, and ReleaseFast modes to ensure there is no unexpected behaviour.
  • Test results are the combined results for 256-bit (f64x4), 512-bit (f64x8) and autodetected width (f64xN) vector implementations.

Test details can be found in src/tests.zig, but as a general overview:

  • Ranges tested: -1..1, -50..50, -1e4..1e4, -1e27..1e27
  • Test types: reproducible (RNG with set seed), non-reproducible (RNG seeded from current time)
  • Samples: 10k per test type per range (i.e. 80k values tested per function, per test run)

Unary Functions

Inverse hyperbolic trigonometric functions (atanh, acosh, asinh) are tested against a modified version of the standard library baseline, as the stdlib implementations can provide incorrect values for out-of-domain inputs.

Function 1e-10 1e-11 1e-13
ln / @log
log2
log10
exp2
exp
cbrt
atan
asin
acos
tanh
sinh
cosh
atanh
asinh
acosh
sign (full)
sign (partial)

Binary Functions

Function 1e-10 1e-11 1e-13
log
pow
atan2
copysign

Performance comparison

Benchmarking conditions:

  • CPU: Intel Core i7-12700H
  • Arch: x86_64-linux
  • OS: NixOS Yarara (26.05.20260316)
  • Zig version: 0.16.0
  • ReleaseSafe and ReleaseFast benchmarks were compiled with -Dtarget=native -Dcpu-native
  • Benchmarks pinned to a single CPU core with taskset --cpu-list 0 (Alderlake P-core, average benchmark frequency: 4.6 GHz)
  • Dataset: array of 10k random f64x4 items (~320kB per pass, likely residing in L2 cache), timings averaged over 512 passes

Only the f64x4 implementations have been benchmarked so far.

SIMD operators in the Zig standard library

Input range: [0,1)

Function Debug Speedup ReleaseSafe Speedup ReleaseFast Speedup
Ln, @log 0.25x 4.71x 4.71x
Log2 0.32x 7.35x 7.34x
Log10 0.34x 7.51x 7.49x
Mod 22.55x 55.38x 54.40x
Exp2 0.22x 3.09x 3.57x
Exp 0.35x 4.64x 5.28x

Input range: [-5,5)

Function Debug Speedup ReleaseSafe Speedup ReleaseFast Speedup ReleaseFast Time Zig ReleaseFast Time fvml
Ln, @log 0.32x 7.74x 7.69x 21.9 ns 2.87 ns
Log2 0.31x 8.09x 8.07x 22.2 ns 2.75 ns
Log10 0.32x 7.74x 7.75x 22.6 ns 2.85 ns
Mod 32.61x 50.25x 50.78x 88.0 ns 1.75 ns
Exp2 0.32x 4.59x 5.26x 26.2 ns 4.90 ns
Exp 0.48x 6.36x 7.05x 37.2 ns 5.23 ns

From this point onwards the performance comparison is consistent except for the exp function, which doubles in performance from ranges of [-5e3,5e3) to ranges of [-5e26,5e26), after which point the performance regresses to half of its [-5,5) speedup.

Notes on the speedup

  • By targeting a guaranteed relative error of 1e-10 rather than full IEEE-754 compliance, fvml can eliminate a lot of the branches that are used in Zig's standard library.
  • By profiling with perf, we noticed the standard library is prone to branch misses and high CPU cycle counts: over the entire benchmark, the standard library functions accounts for 99.83% of branch misses, and 83.95% of CPU cycles.

N-Ary Operators with ILP Variants

ILP: Instruction-level parallelism.

ILP-variants do not guarantee a performance increase, as it is very much hardware-specific. However, on many modern CPUs, they should be beneficial, as seen in these benchmarks. In a worst-case scenario, they should not be any slower than the non-ilp variants.

Function Naive Variant Time ILP Variant Time ILP Speedup
sum 0.439 ns / f64x4 0.177 ns / f64x4 2.49x
product 0.874 ns / f64x4 0.221 ns / f64x4 3.96x
arithmetic_mean 0.439 ns / f64x4 0.177 ns / f64x4 2.49x
geometric_mean_unstable 0.879 ns / f64x4 0.224 ns / f64x4 3.92x
hypot 0.876 ns / f64x4 0.227 ns / f64x4 3.86x

Minimum Supported Zig Version (MSZV)

The minimum supported version of Zig for fvml is 0.16.0. It is also currently the only tested version.

License

This project is licensed under the EUPL v1.2 license

Attribution

  • Remez.jl: This Julia library was used to generate the polynomial coefficients for minimax function approximations.

Miscellaneous

About the dot in "math·s"

Just for some fun, I decided to borrow the dot from new(ish) changes to the French language, where it can be used to include both versions of a gendered noun. As I prefer to use "maths", but many people refer to it as "math", this felt like a reasonable compromise.