Zig SIMD Strings
A SIMD string is a short piece of text held in a vector of bytes. Each byte lane holds one character, so a whole chunk can be checked at once.
Check two chunks for equality
This program compares two eight-byte chunks and reports whether every lane matches.
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
const a: @Vector(8, u8) = .{ 'h', 'e', 'l', 'l', 'o', '!', '!', '!' };
const b: @Vector(8, u8) = .{ 'h', 'e', 'l', 'l', 'o', '!', '!', '!' };
const c: @Vector(8, u8) = .{ 'h', 'e', 'l', 'l', 'o', 'x', 'x', 'x' };
try out.print("{}\n", .{@reduce(.And, a == b)});
try out.print("{}", .{@reduce(.And, a == c)});
}Count matching lanes
This program counts how many lanes match between two eight-byte chunks.
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
const a: @Vector(8, u8) = .{ 'h', 'e', 'l', 'l', 'o', '!', '!', '!' };
const c: @Vector(8, u8) = .{ 'h', 'e', 'l', 'l', 'o', 'x', 'x', 'x' };
const eq = a == c;
const one: @Vector(8, u8) = @splat(1);
const zero: @Vector(8, u8) = @splat(0);
const ones = @select(u8, eq, one, zero);
try out.print("{d}", .{@reduce(.Add, ones)});
}Uppercase a chunk
This program turns lowercase lanes to uppercase and prints the chunk as text.
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
const lower: @Vector(8, u8) = .{ 'z', 'i', 'g', ' ', 'r', 'o', 'c', 'k' };
const low_a: @Vector(8, u8) = @splat('a');
const diff: @Vector(8, u8) = @splat(32);
const upper = @select(u8, lower >= low_a, lower - diff, lower);
const chars: [8]u8 = upper;
try out.print("{s}", .{chars});
}Example
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
const lower: @Vector(8, u8) = .{ 'z', 'i', 'g', '!', '!', '!', '!', '!' };
const low_a: @Vector(8, u8) = @splat('a');
const diff: @Vector(8, u8) = @splat(32);
const upper = @select(u8, lower >= low_a, lower - diff, lower);
const chars: [8]u8 = upper;
try out.print("{s}", .{chars});
}Key points
- Byte lane: a byte lane is one character slot in a vector of bytes.
- Comparing two byte vectors compares matching lanes, and the result is a mask of true and false values.
- A SIMD string can be checked for equality by combining its comparison mask with @reduce and .And.
- Counting matches uses a select that turns matching lanes into 1 and other lanes into 0, then adds them with @reduce.
- Changing case uses a select that subtracts 32 from lanes that hold lowercase letters and leaves other lanes alone.
- A byte vector converts directly to an array, so the result can be printed as ordinary text.
The code builds an eight-byte chunk holding lowercase letters and marks. @select subtracts 32 only from lanes at or above lowercase a. The other lanes stay the same, and the array conversion lets the result print as text.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
How many lanes match between hello!!! and helloxxx as eight-byte chunks?