Vectors

A vector is a group of booleans, Integers, Floats, or Pointers which are operated on in parallel using SIMD instructions. Vector types are created with the builtin function @Type, or using the shorthand function std.meta.Vector.

Vectors support the same builtin operators as their underlying base types. These operations are performed element-wise, and return a vector of the same length as the input vectors. This includes:

  • Arithmetic (+, -, /, *, @divFloor, @sqrt, @ceil, @log, etc.)
  • Bitwise operators (>>, <<, &, |, ~, etc.)
  • Comparison operators (<, >, ==, etc.)

It is prohibited to use a math operator on a mixture of scalars (individual numbers) and vectors. Zig provides the @splat builtin to easily convert from scalars to vectors, and it supports @reduce and array indexing syntax to convert from vectors to scalars. Vectors also support assignment to and from fixed-length arrays with comptime known length.

For rearranging elements within and between vectors, Zig provides the @shuffle and @select functions.

Operations on vectors shorter than the target machine’s native SIMD size will typically compile to single SIMD instructions, while vectors longer than the target machine’s native SIMD size will compile to multiple SIMD instructions. If a given operation doesn’t have SIMD support on the target architecture, the compiler will default to operating on each vector element one at a time. Zig supports any comptime-known vector length up to 2^32-1, although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may result in compiler crashes on current versions of Zig.

vector_example.zig

  1. const std = @import("std");
  2. const Vector = std.meta.Vector;
  3. const expectEqual = std.testing.expectEqual;
  4. test "Basic vector usage" {
  5. // Vectors have a compile-time known length and base type,
  6. // and can be assigned to using array literal syntax
  7. const a: Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
  8. const b: Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
  9. // Math operations take place element-wise
  10. const c = a + b;
  11. // Individual vector elements can be accessed using array indexing syntax.
  12. try expectEqual(6, c[0]);
  13. try expectEqual(8, c[1]);
  14. try expectEqual(10, c[2]);
  15. try expectEqual(12, c[3]);
  16. }
  17. test "Conversion between vectors, arrays, and slices" {
  18. // Vectors and fixed-length arrays can be automatically assigned back and forth
  19. var arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
  20. var vec: Vector(4, f32) = arr1;
  21. var arr2: [4]f32 = vec;
  22. try expectEqual(arr1, arr2);
  23. // You can also assign from a slice with comptime-known length to a vector using .*
  24. const vec2: Vector(2, f32) = arr1[1..3].*;
  25. var slice: []const f32 = &arr1;
  26. var offset: u32 = 1;
  27. // To extract a comptime-known length from a runtime-known offset,
  28. // first extract a new slice from the starting offset, then an array of
  29. // comptime known length
  30. const vec3: Vector(2, f32) = slice[offset..][0..2].*;
  31. try expectEqual(slice[offset], vec2[0]);
  32. try expectEqual(slice[offset + 1], vec2[1]);
  33. try expectEqual(vec2, vec3);
  34. }

Shell

  1. $ zig test vector_example.zig
  2. 1/2 test "Basic vector usage"... OK
  3. 2/2 test "Conversion between vectors, arrays, and slices"... OK
  4. All 2 tests passed.

TODO talk about C ABI interop
TODO consider suggesting std.MultiArrayList

See also: