fearless_simd v0.7:64位整数、改进泛型与SSE2,v1.0即将到来
fearless_simd v0.7:64位整数、改进的泛型、SSE2,以及即将到来的v1.0
Shnatsel,2026年8月10日
fearless_simd 将 unsafe 从 SIMD 中移除。
无论你追求何种抽象层次——自动向量化与多版本化、可移植 SIMD,还是仅需安全访问原始内建函数——fearless_simd 都能满足需求!
它零依赖、构建时间短、公共 API 安全,并且底层 unsafe 代码极少——比同类方案少几个数量级!
v0.7 的主要新增功能包括:支持 64 位整数、在 x86 上以显式 SSE2 级别替代标量回退、改进泛型编程支持,以及实现更多 SIMD 操作。
这也是 Fearless SIMD v1.0 之前的最后一个主要版本,除非 API 方面出现任何问题。
64位整数
fearless_simd 的整个 API 接口现已扩展到 u64 和 i64 向量类型。
早期版本因硬件支持不完善而未提供 64 位整数向量。例如,AVX2 对 64 位整数的许多操作缺乏硬件支持,因此需要利用现有 SIMD 指令进行模拟,才能获得不错的性能。
跟踪哪些内建函数属于哪个指令集也颇具挑战,一旦出错便可能导致内存安全问题。然而,在 v0.5 中,我们让编译器替我们跟踪,这大幅减少了 fearless_simd 中的 unsafe 代码块,也让实现硬件支持不均的操作变得更加容易。
所有其他整数向量类型(i8、u8、i16、u16、i32、u32)以及 f32/f64 在之前的版本中均已支持,因此 64 位整数是完整类型覆盖的最后一块拼图。一旦 f16 类型在标准库中稳定,我们将研究对其的支持。
更多操作
swizzle_dyn现在已对所有位宽实现,可支持任意字节重排。我还为std::simd提交了相关性能改进。和std::simd不同的是,Fearless SIMD 同时支持越界索引归零,以及返回实现定义(但内存安全)的结果——如果你能确保所有索引都在范围内,后者在某些平台上比归零更便宜。- 所有类型现在都可以进行拓宽和收窄转换;例如在 SIMD 代码中,你可以将
u8的向量转换为u16,或将u16转换为u8。收窄转换时,你可以选择像as操作符那样的环绕截断、饱和截断,或者使用平台所提供的最廉价的转换方式(在你确信值能放入更窄类型时很有用)。 - 新增了便捷函数
shift_elements_left、shift_elements_right、rotate_elements_left和rotate_elements_right,以便与std::simd的 API 更好地兼容。这些操作其实也可以通过slide来实现,但用专门的函数表达意图会更清晰。
泛型编程的改进
对泛型编程(即编写与具体向量类型无关的通用函数)的支持有了大幅提升。以下仅列举几项要点:
SimdBasetrait 现在同时抽象了整数向量和浮点向量,并实现了所有既适用于整数又适用于浮点数的方法。- 对
Bytestrait 的改进,使得 SIMD 向量之间可以进行泛型 bitcast(安全的 transmute)。 - SIMD 类型上的每一个操作现在都通过 trait 暴露,不再有任何操作只针对具体类型实现。
SimdBase::Element和SimdBase::Array等关联类型现在编码了大量泛型约束,以便能对这些类型执行泛型操作。
完整变更日志请参阅此处。总体而言,现在使用泛型进行 SIMD 编程更加顺手,也能更方便地以泛型方式表达各种算法。
这些改进同样惠及通过宏来抽象 SIMD 向量类型的用户,例如,他们可以编写 $type::from_slice,而不是 T::from_slice。由于所有操作现在都直接由类型本身提供,因此不再需要使用 paste 等额外 crate 将类型注入函数名。
显式支持 SSE2
如今,不支持 SSE4.2 的 x86 系统已经非常罕见。不过,由于 SSE2 属于 x86_64 和 i686 Rust 目标的基线指令集,因此无需运行时分派或多版本处理,即可假定系统支持 SSE2。某些 crate 只需要非常有限的向量指令,并不会从后续扩展中获益,所以放弃运行时分派可以简化代码并减小二进制体积。
为了更好地支持这一用例,Fearless SIMD 现在新增了显式的 Sse2 级别,其中操作通过 SIMD intrinsic 实现,而不再依赖在无法使用 SSE4.2 时对 Fallback 级别进行自动向量化。当用户选择在运行时挑选最佳 SIMD 实现时,这一改进能够提升 x86 上的性能。
在二级 i586 目标上,SSE2 仍需在运行时检测,也可以通过常规的多版本控制选项将其禁用。
构建时间改进
尽管新增了 64 位整数向量、更多受支持的操作,以及一整套全新的 SSE2 SIMD 级别,fearless_simd 作为依赖项时的编译时间仍与 v0.6 持平:x86 从零开始编译需 2 秒,AArch64 则需 1 秒。测试是在我的 Zen 4 台式机 CPU 上进行的:在仅依赖 fearless_simd 的空 crate 中运行 cargo clean && cargo build --release --timings。
尽管新增了不少功能,编译时间却保持不变,这背后是一轮构建性能分析与优化。否则在我的机器上 x86 构建时间会涨到 3.4 秒。虽然对于一次全新 release 构建来说这个数字也不算多,而且只要一个 crate 的其他依赖链编译时间超过 3.4 秒,这点增长完全会被淹没。但我认为,保持较低的构建时间对于 Fearless SIMD 成为一套基础级 SIMD 抽象至关重要。这也是 fearless_simd 自身不引入任何依赖的重要原因之一。
fearless_simd API 中绝大部分都是泛型函数。它们只有在被实例化时才会生成机器码,所以如果不使用它们,就不会在构建时间和二进制体积上付出任何代价。编译器只需要对它们做类型检查和借用检查,因此构建时间几乎完全受前端瓶颈制约。在 nightly Rust 上使用 RUSTFLAGS=-Zthreads=8 启用并行前端后,编译时间直接砍半,所以一旦并行前端进入 stable 工具链,我们还有进一步的提速空间可以期待。
1.0 之前的 API 清理
本版本移除了针对具体类型的 reinterpret_* 方法。这些方法只覆盖了少数类型,而且每对类型组合都需要单独写一个方法,实在不实用。因此它们被直接砍掉,统一用泛型版的 bitcast 方法代替。load_interleaved_128 操作被重命名为 load_four_interleaved,让意图更清晰。
这些都算是比较小的 API 清理工作,而 API 本身已经稳定运行将近一年了。除了近期对泛型的改动之外,这套 API 已经在真实项目中得到验证:在 crates.io 上有十多个直接依赖,在 GitHub 上有超过一千个仓库直接或间接地在使用它。
我们对这套 crate 的设计很有信心,是时候正式定稿了。如果没有收到关于 API 的反馈意见,我们将在九月初发布 Fearless SIMD v1.0。
v1.0 不会再有进一步的功能变更,我们推迟这次发布仅仅是为了在 API 定稿之前留出一段时间收集社区反馈。
我们想听听你的声音!
我们相信 fearless_simd 有望成为 Rust 生态中首选的基础 SIMD crate,无论你需要的是自动向量化、可移植 SIMD,还是安全地使用 intrinsics,亦或是以上全部。这个目标如今已触手可及。
我们比以往任何时候都更希望听到你的反馈!有没有哪些 API 用起来别扭或缺失的地方?还是一切都很顺畅?欢迎随时告诉我们!