Zig之标量、向量与SIMD

Zig 中,标量(Scalar)和向量(Vector)的区分不仅是数学概念上的差异,更是硬件指令集(SIMD)在语言层面的直接映射 。向量(Vector)是为 SIMD(单指令多数据) 并行计算提供的一等公民。

标量(Scalar)

标量是单一、独立的基本数据值,是 Zig 所有运算的基础原子类型。

标量类型体系

  • 布尔型:bool(值:true/false
  • 整型(有符号 / 无符号,位宽明确):
    • 有符号:i8/i16/i32/i64/i128/isize
    • 无符号:u8/u16/u32/u64/u128/usize
  • 浮点型:f16/f32/f64/f128
  • 指针:*T/*const T(地址型标量)

标量核心特性

  • 单一值:一个变量仅存一个数据,无内部并行结构。
  • 原生运算:支持常规算术、位运算、比较。
  • 内存布局:连续、紧凑,无额外开销(如 u8 占 1 字节,f32 占 4 字节)。

向量(Vector)

向量是固定长度、同类型标量的集合,通过单指令多数据(SIMD) 并行执行元素级运算,直接映射 CPU 硬件加速指令。

  • 语法:@Vector(comptime N: usize, T: type)
    • N:元素个数(编译期常量,建议 2/4/8/16 等 2 的幂,匹配 CPU SIMD 宽度)
    • T:元素类型(仅支持 bool/ 整型 / 浮点 / 指针)

定义与初始化

  • 直接初始化:用 .{} 按元素赋值

    const v: @Vector(3, i32) = .{10, 20, 30};

  • 广播(@splat):标量扩展

    const scalar: f32 = 2.0;
    const vec = @as(@Vector(4, f32), @splat(scalar)); // 等价 .{2.0,2.0,2.0,2.0}

运算

向量重载了普通算术、位运算和比较运算符, 所有运算符按元素独立执行,自动编译为 SIMD 指令(如 x86 的 SSE/AVX、ARM 的 NEON)。

算术 / 位运算

必须是向量间运算,变量需先要扩展、转换为向量

复制代码
const a: @Vector(4, f32) = .{ 1.0, 2.0, 3.0, 4.0 };
const b: @Vector(4, f32) = .{ 5.0, 6.0, 7.0, 8.0 };
const c = a + b; // .{6.0,8.0,10.0,12.0}
const d = c * @as(@Vector(4, f32), @splat(2.0)); // .{12.0,16.0,20.0,24.0}
比较运算

比较的结果是一个布尔向量,元素类型为 bool,长度与操作数相同。

zig

复制代码
const a: @Vector(4, i32) = .{ 1, 5, 3, 8 };
const cond = a2 > @as(@Vector(4, i32), @splat(2));      // {false, true, true, true}  (布尔向量)

布尔向量主要用于后面的 @select 选择操作。

元素访问与内建函数

索引单个元素

像数组一样用 [] 访问(只能读,不能直接通过索引修改)。

复制代码
const v: @Vector(4, f64) = .{ 1.1, 2.2, 3.3, 4.4 };
const x = v[2];   // 3.3  (标量)

若要修改某个元素,通常借助 @shuffle 或转换为数组再操作。

@reduce -- 归约操作

将整个向量归约为一个标量。支持 .Min, .Max, .Add, .Mul, .And, .Or 等。

复制代码
const sum = @reduce(.Add, a);    // 1+2+3+4 = 10
const min = @reduce(.Min, a);    // 1
@shuffle -- 重排元素

用编译期掩码从一到两个向量中选取元素,生成新向量。

复制代码
const v1: @Vector(4, i32) = .{ 1, 2, 3, 4 };
const v2: @Vector(4, i32) = .{ 5, 6, 7, 8 };
// 掩码使用负数表示从 v2 选取 ( -1 代表 v2[0], -2 代表 v2[1] ... )
const shuffled = @shuffle(i32, v1, v2, [_]i32{ 0, -1, 3, -3 });
// 结果: { v1[0], v2[0], v1[3], v2[2] } = {1, 5, 4, 7}
@select -- 按条件选择

根据布尔向量在两个向量之间挑选元素:true 选第一个向量的元素,false 选第二个向量的元素

复制代码
const mask: @Vector(4, bool) = .{ true, false, false, true };
const a: @Vector(4, i32) = .{ 1, 2, 3, 4 };
const b: @Vector(4, i32) = .{ 10, 20, 30, 40 };
const result = @select(i32, mask, a, b);
// 结果: {1, 20, 30, 4}   true 选 a,false 选 b
const c = @select(i32, mask, a, @as(@Vector(4, i32), @splat(0))); // {1, 0, 0, 4}

与数组转换

向量可以显式强制转换为等长的数组,反之亦然:

  • 转换不涉及任何代码生成,只是重新解释了内存中的同一份数据

  • 利用数组可以方便地对向量元素进行写操作。

    const arr: [4]f32 = [_]f32{ 1.0, 2.0, 3.0, 4.0 };
    const vec_from_arr: @Vector(4, f32) = arr; // 数组→向量
    const arr2: [4]f32 = vec_from_arr; // 向量→数组

与数组异同

特性 数组 [N]T 向量 @Vector(N, T)
本质定位 数据存储结构 (Memory-centric) 计算原语 (Compute-centric)
硬件映射 内存地址空间 / 通用寄存器 SIMD 向量寄存器 (如 XMM/YMM/ZMM)
运算行为 必须通过循环或迭代逐个处理 原生支持元素级并行运算 (+, -, *, /)
内存布局 紧凑排列,遵循标准对齐 高度对齐 (通常由编译器根据硬件自动提升)
灵活性 支持任意类型 T (含结构体、指针) 仅限标量类型 (整数、浮点、布尔、指针)
访问方式 索引访问 arr[i] 索引访问 vec[i] (但主要用于批处理)

向量内存与性能

  • 内存对齐:自动按向量宽度对齐(如 128 位向量对齐 16 字节,256 位对齐 32 字节),满足 SIMD 硬件要求。
  • 并行效率:一次指令处理 N 个数据(如 4 路 f32 向量一次算 4 个加法),吞吐量是标量循环的 N 倍(无额外开销)。
  • 适用场景:图像处理、音频滤波、物理模拟、密码学、批量数据处理(循环体无依赖)。

SIMD

SIMD = Single Instruction, Multiple Data(单指令、多数据):一条指令,同时对多个数据做相同的操作。

像素处理、矩阵乘法、音频滤波、字符串匹配、CRC 计算等场景里,对数组或缓冲区的每个元素施加几乎相同的操作,且元素间无依赖关系,存在天然的数据并行度。

现代 CPU 提供专门的向量寄存器和向量指令集:

平台 指令集示例 向量寄存器宽度 可容纳的常见元素组合
x86-64 MMX / SSE / AVX / AVX-512 64/128/256/512 bit 4×f32, 2×f64, 8×i16
ARM NEON 128 bit 4×f32, 2×f64, 8×i16
RISC-V V 扩展 (RVV) 可配置(通常 128~1024 bit) 灵活长度

一条 ADDPS(SSE 加法)就是对两个 128 位寄存器中的 4 个 f32 同时相加。

向量 SIMD 与普通标量计算间差异

Zig 代码与 SIMD 对应示例

Zig 代码 x86-64 SIMD 示例指令
v1 + v2 paddd / addps / paddw
v1 * v2 pmulld / mulps
v1 & v2 pand
v1 > v2 pcmpgtd / cmpltps(生成掩码)
@reduce(.Add, v) 水平加:haddps + shufps 等序列
@shuffle(i32, v1, v2, mask) shufps / pshufd / vperm
@select(i32, mask, a, b) blendvps / bsl (AND/ANDNOT/OR 模拟)
相关推荐
深念Y4 天前
哈希与向量:计算机理解现实的两座桥梁
人工智能·数学·机器学习·向量·hash·哈希·空间
BestOrNothing_20157 天前
C++零基础到工程实战(4.3.8):基于 vector 实现一个简易缓存数据库
c++·vector·string·缓存数据库·stringstream·键值存储·getline
weixin_4460235611 天前
C语言过时了?2026年C3和Zig谁能拯救它
c语言·zig·c3·系统级开发·语言革新
BestOrNothing_201512 天前
C++零基础到工程实战(4.3.4):vector数组搜索、删除、插入与排序
c++·vector·sort·find·insert·动态数组·erase
BestOrNothing_201513 天前
C++零基础到工程实战(4.3.3):vector数组访问与遍历
c++·迭代器·stl·vector·动态数组
深念Y13 天前
图数据库 vs 向量数据库:AI时代的两个“最强大脑”
数据库·人工智能·neo4j·图论··向量·rag
BestOrNothing_201514 天前
C++零基础到工程实战(4.3.1):数组与vector初识——连续内存与动态数组的本质解析
c++·vector·初始化·内存分配·栈区数组·堆区数组
深念Y21 天前
从字典到向量:索引技术的演进
向量·es·索引·倒排索引·向量数据库·字典·倒排文件索引
奶人五毛拉人一块23 天前
模板与vector的学习
数据结构·学习·迭代器·vector·模板