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?