m03 · Burst · reading · 8 min
Unity.Mathematics — writing in vector width on purpose
Vectors as first-class values
The last two lessons framed vectorization as something the compiler
might do to your loop if you keep it clean. Unity.Mathematics lets
you stop hoping. It provides types — float2, float3, float4,
int4, float4x4, quaternion — that are SIMD vectors, and
arithmetic on them maps to vector instructions directly:
using Unity.Mathematics;
float4 a = new float4(1f, 2f, 3f, 4f);
float4 b = new float4(10f, 20f, 30f, 40f);
float4 c = a + b; // one vector add — four lanes, one instruction
That a + b is not a loop the compiler has to analyze and prove
vectorizable. It’s four-wide arithmetic by construction; under Burst it
compiles to a single vaddps-class instruction. When you express your
math in these types, you are handing the compiler data already in vector
shape — the most reliable path to vector code, because there’s nothing to
infer. The types make the SIMD explicit rather than emergent.
This reframes a lot of numeric code. A position update on a float3
position and float3 velocity is one vector multiply-add, not three
scalar ones. A dot product, a normalize, a matrix transform — each is a
handful of vector ops on these types instead of a hand-written scalar
loop the compiler has to rescue. You write the math at the width the
hardware runs it.
Why it looks like shader code
If you’ve written HLSL, Unity.Mathematics will feel familiar on
purpose. The types (float4, float4x4), the swizzles (v.xyz,
v.xxzz), the function names (math.dot, math.normalize,
math.lerp, math.saturate), and the column-vector math conventions all
mirror shading languages. This isn’t cosmetic. Gameplay and graphics
math are the same math, and a team that expresses both in the same
vocabulary carries fewer translation errors between CPU simulation and
GPU rendering, and reasons about both with one mental model. The
math.* free functions and the lower-case type names are a deliberate
break from typical C# naming precisely to keep that shader-shaped
consistency intact.
The swizzles are worth calling out as more than convenience:
float3 p = transform.Position;
float2 groundPlane = p.xz; // pull two lanes, no scalar shuffling
float3 flipped = p.zyx; // reorder lanes in one operation
A swizzle is a lane operation the hardware does directly, not a set of
scalar copies you’re writing by hand. Reaching into .x, .y, .z
individually and recombining them is often slower and less clear than
the swizzle that expresses the same rearrangement in vector terms.
The choices that are actually about speed
Two everyday decisions with these types are performance decisions wearing the costume of style:
float3 vs float4. A float3 is three lanes; the hardware vector
is four (or eight). Sometimes a float3 is padded to float4 width
anyway, so the fourth lane is “free” — and sometimes an algorithm is
cleaner and faster expressed as a float4 that uses all four lanes than
as a float3 that wastes one. This isn’t a rule to memorize; it’s an
awareness that lane count and hardware width interact, and that the
“natural” 3-component choice isn’t automatically the fastest one. Measure
when it’s hot.
math.* intrinsics over hand-rolled scalar math. math.length(v),
math.distance(a, b), math.normalize(v), math.mad(a, b, c) (a fused
multiply-add) exist because each maps to an efficient vector sequence,
often a single hardware instruction. Writing out sqrt(x*x + y*y + z*z)
by hand gives the compiler a scalar expression to rescue; calling
math.length hands it the vector idiom directly. The intrinsics are the
vocabulary the optimizer recognizes.
What this buys you
You can now write numeric code that is vectorized on purpose — in the
same vocabulary as your shaders — and you know the two quiet performance
choices (lane count, intrinsic vs hand-rolled) that hide inside these
types. The final lesson of the module closes the loop on verification:
how to read the assembly Burst actually generated and how to benchmark it
without fooling yourself, so “I think it vectorized” becomes “I can see
the vmulps and I measured the speedup.”