Mojo SIMD Types

SIMD stands for Single Instruction, Multiple Data. It is a hardware feature that lets one CPU instruction operate on many values simultaneously instead of one at a time. Mojo exposes SIMD directly in its type system, giving you GPU-class arithmetic speed on a regular CPU.

The Factory Line Analogy

Scalar (one at a time):
  Worker processes one box, then the next, then the next...
  Box 1 → done
  Box 2 → done
  Box 3 → done
  Box 4 → done
  Total: 4 steps

SIMD (all at once):
  Machine processes 4 boxes in one press
  [Box1, Box2, Box3, Box4] → all done in 1 step
  Total: 1 step

SIMD is 4× faster here. Real hardware SIMD can handle 8, 16, or 32 at once.

Declaring a SIMD Value

fn main():
    var v = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)
    print(v)   # [1.0, 2.0, 3.0, 4.0]

The declaration has two parts inside the square brackets:

SIMD[ DType.float32, 4 ]
      │               │
      │               └── Width: how many elements (must be power of 2)
      └── DType: the element type

Available DTypes

DType          | Element Type | Bits Per Element
---------------|--------------|------------------
DType.int8     | Int8         |  8
DType.int16    | Int16        | 16
DType.int32    | Int32        | 32
DType.int64    | Int64        | 64
DType.uint8    | UInt8        |  8
DType.float16  | Float16      | 16
DType.float32  | Float32      | 32
DType.float64  | Float64      | 64
DType.bool     | Bool         |  1

SIMD Arithmetic

All standard arithmetic operators work on SIMD types element-by-element. One Mojo operator call maps to one hardware instruction.

fn main():
    var a = SIMD[DType.float32, 4](10.0, 20.0, 30.0, 40.0)
    var b = SIMD[DType.float32, 4]( 1.0,  2.0,  3.0,  4.0)

    var sum  = a + b
    var diff = a - b
    var prod = a * b
    var quot = a / b

    print(sum)   # [11.0, 22.0, 33.0, 44.0]
    print(diff)  # [9.0, 18.0, 27.0, 36.0]
    print(prod)  # [10.0, 40.0, 90.0, 160.0]
    print(quot)  # [10.0, 10.0, 10.0, 10.0]
Element-wise addition diagram:
  a: [ 10  20  30  40 ]
  b: [  1   2   3   4 ]
     ──────────────────
  +: [ 11  22  33  44 ]
  (4 additions happen simultaneously in hardware)

Splat: Filling All Lanes with One Value

The splat method creates a SIMD value where every lane holds the same number. This is useful for broadcasting a scalar into a vector operation.

fn main():
    # Fill all 8 lanes with the value 3.0
    var threes = SIMD[DType.float32, 8].splat(3.0)
    print(threes)   # [3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0]

    var data = SIMD[DType.float32, 8](1, 2, 3, 4, 5, 6, 7, 8)
    var tripled = data * threes
    print(tripled)   # [3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0, 24.0]
Splat diagram:
  scalar 3.0 → splat →  [3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0]
                         ─────────────────────────────────────────
  SIMD *:  [1,2,3,4,5,6,7,8] × [3,3,3,3,3,3,3,3]
         = [3,6,9,12,15,18,21,24]

Accessing Individual Elements

fn main():
    var v = SIMD[DType.int32, 4](100, 200, 300, 400)
    print(v[0])   # 100
    print(v[3])   # 400

Reduction Operations

Reduction operations collapse all elements of a SIMD vector into one scalar value.

fn main():
    var v = SIMD[DType.float32, 4](10.0, 5.0, 8.0, 3.0)

    var total = v.reduce_add()   # 26.0
    var maxv  = v.reduce_max()   # 10.0
    var minv  = v.reduce_min()   # 3.0

    print(total, maxv, minv)
reduce_add on [10, 5, 8, 3]:

  Round 1: [10+5, 8+3] → [15, 11]
  Round 2: [15+11]     → [26]
  Result: 26

  This tree reduction takes log₂(4) = 2 steps
  instead of 3 sequential additions.

Practical Example: Dot Product

A dot product multiplies two vectors element-by-element and sums the results. It appears in nearly every AI model computation.

fn dot_product() -> Float32:
    var a = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)
    var b = SIMD[DType.float32, 4](5.0, 6.0, 7.0, 8.0)

    var products = a * b        # [5, 12, 21, 32]
    return products.reduce_add()  # 5+12+21+32 = 70.0

fn main():
    print(dot_product())   # 70.0
Step 1 — element-wise multiply:
  a: [1, 2, 3, 4]
  b: [5, 6, 7, 8]
  →  [5,12,21,32]

Step 2 — reduce_add:
  5 + 12 + 21 + 32 = 70

SIMD Width and Hardware

Typical SIMD widths by hardware:
  SSE (old Intel x86):   128 bits → 4 × Float32
  AVX2 (modern Intel):   256 bits → 8 × Float32
  AVX-512 (server CPUs): 512 bits → 16 × Float32
  ARM NEON (mobile/Mac): 128 bits → 4 × Float32

The Mojo compiler targets the SIMD width of the CPU you compile for.
Choose widths that are powers of 2: 1, 2, 4, 8, 16, 32.

Key Takeaways

SIMD types hold multiple values of the same element type and process them all with one hardware instruction. Declare SIMD values with SIMD[DType.xxx, width] where width is a power of 2. Arithmetic operators work element-by-element. Use splat() to broadcast one value to all lanes. Reduction methods like reduce_add() collapse the vector to a scalar. SIMD is the core mechanism that lets Mojo outperform Python by 100× or more on numerical workloads.

Leave a Comment

Your email address will not be published. Required fields are marked *