🎓 A1Learner
🔍
0 of 46 lessons completed

Zig SIMD Patterns

A SIMD pattern is a reusable way to choose or change lanes with a mask. A mask decides lane by lane, so one program can handle many cases at once.

Pick lanes with a mask

This program picks lanes from the first vector where the mask is true, and from the second vector elsewhere.

const std = @import("std");
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const a: @Vector(4, i32) = .{ 1, 2, 3, 4 };
    const b: @Vector(4, i32) = .{ 10, 20, 30, 40 };
    const mask: @Vector(4, bool) = .{ true, false, true, false };
    const picked = @select(i32, mask, a, b);
    try out.print("{d} {d} {d} {d}", .{ picked[0], picked[1], picked[2], picked[3] });
}

Cap lanes from above

This program caps every lane at 3 by picking the lane itself where it is below 3, and 3 elsewhere.

const std = @import("std");
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const a: @Vector(4, i32) = .{ 1, 2, 3, 4 };
    const three: @Vector(4, i32) = @splat(3);
    const capped = @select(i32, a < three, a, three);
    try out.print("{d} {d} {d} {d}", .{ capped[0], capped[1], capped[2], capped[3] });
}

Clamp both ends

This program raises lanes below 0 up to 0, then lowers lanes above 10 down to 10.

const std = @import("std");
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const v: @Vector(4, i32) = .{ -5, 3, 15, 7 };
    const zero: @Vector(4, i32) = @splat(0);
    const ten: @Vector(4, i32) = @splat(10);
    const low = @select(i32, v < zero, zero, v);
    const clamped = @select(i32, low > ten, ten, low);
    try out.print("{d} {d} {d} {d}", .{ clamped[0], clamped[1], clamped[2], clamped[3] });
}

Example

const std = @import("std");
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const a: @Vector(4, i32) = .{ 5, 2, 9, 1 };
    const b: @Vector(4, i32) = .{ 3, 8, 4, 7 };
    const larger = @select(i32, a > b, a, b);
    try out.print("{d} {d} {d} {d}", .{ larger[0], larger[1], larger[2], larger[3] });
}

Key points

  • Mask: a mask is a vector of true and false values, one per lane.
  • Select: a select is an operation that picks one value per lane from two vectors using a mask.
  • In Zig, @select takes an element type, a mask, and two vectors of that type.
  • A true lane picks from the first vector, and a false lane picks from the second vector.
  • A comparison between vectors builds a mask, so the mask can come from the data itself.
  • Two selects in a row can clamp a value, first raising low lanes and then lowering high lanes.

The code builds two vectors and compares them lane by lane. The comparison gives a mask that is true where the first lane is larger. @select then picks the larger lane from each pair.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

For a mask of true, false and vectors a holding 7, 8 and b holding 1, 2, what does @select produce?