8个版本
0.1.4 | 2022年4月2日 |
---|---|
0.1.3 | 2021年5月14日 |
0.1.1 | 2021年4月26日 |
0.0.3 | 2021年4月21日 |
#31 in 编码
1,459,932 每月下载量
在 817 个crate中使用 (60直接使用)
97KB
2K SLoC
simdutf8 – 高速UTF-8验证
使用SIMD扩展的Rust高速UTF-8验证,基于simdjson的实现。最初由simd-json.rs的开发者移植到Rust,但现在得到了大幅改进。
状态
此库已用样本数据以及模糊测试进行了彻底测试,并且没有已知的错误。
特性
basic
API,用于最快的验证,针对有效的UTF-8进行了优化compat
API,作为std::str::from_utf8()
的完全兼容替代品- 支持x86和x86-64上的AVX 2和SSE 4.2实现
- 🆕 支持ARM64 (aarch64) SIMD,使用Rust 1.59 (使用功能
aarch64_neon
) 和Nightly (无需额外功能) - 🆕 支持WASM (wasm32) SIMD
- x86-64:在有效非ASCII上比std库快23倍,在ASCII上快四倍
- aarch64:在有效非ASCII上比std库快11倍,在ASCII上快四倍(Apple Silicon)
- 比原始simdjson实现更快
- 根据CPU支持在运行时选择最快的实现(在x86上)
- 如果SIMD扩展不受支持,则回退到出色的std实现
- 纯Rust编写
- 无依赖
- 支持no-std
快速入门
将依赖项添加到您的Cargo.toml文件中
[dependencies]
simdutf8 = "0.1.4"
为Rust 1.59上的ARM64 SIMD支持
[dependencies]
simdutf8 = { version = "0.1.4", features = ["aarch64_neon"] }
使用 simdutf8::basic::from_utf8()
作为 std::str::from_utf8()
的替代。
use simdutf8::basic::from_utf8;
println!("{}", from_utf8(b"I \xE2\x9D\xA4\xEF\xB8\x8F UTF-8!").unwrap());
如果需要关于验证失败的详细信息,请使用 simdutf8::compat::from_utf8()
。
use simdutf8::compat::from_utf8;
let err = from_utf8(b"I \xE2\x9D\xA4\xEF\xB8 UTF-8!").unwrap_err();
assert_eq!(err.valid_up_to(), 5);
assert_eq!(err.error_len(), Some(2));
API
基本版本
使用 basic
API 版本以获得最大速度。它在有效 UTF-8 上运行最快,但只有在处理整个字节序列之后才检查错误,并且不提供有关数据不是有效 UTF-8 的详细信息。simdutf8::basic::Utf8Error
是一个零大小的错误结构。
兼容版本
兼容版本与 std::str::from_utf8()
完全 API 兼容。特别是,simdutf8::compat::from_utf8()
返回一个 simdutf8::compat::Utf8Error
,它具有 valid_up_to()
和 error_len()
方法。第一个对于验证流式数据很有用。第二个例如对于将无效的字节序列替换为替换字符很有用。
它还会提前失败:错误在字符串处理过程中即时检查,并且一旦遇到无效的 UTF-8 序列,它将返回而不会处理剩余的数据。即使输入是有效的 UTF-8,与 basic
API 相比,这也会带来轻微的性能损失。
实现选择
X86
使用 std::is_x86_feature_detected!
宏在运行时选择最快的实现,除非编译器针对的 CPU 支持最快的可用实现。因此,如果在最近的 x86-64 机器上使用 RUSTFLAGS="-C target-cpu=native"
进行编译,则在编译时选择 AVX 2 实现,并且禁用了运行时选择。
对于无标准支持(使用 --no-default-features
编译),实现始终在编译时根据目标 CPU 选择。使用 RUSTFLAGS="-C target-feature=+avx2"
选择 AVX 2 实现,或者使用 RUSTFLAGS="-C target-feature=+sse4.2"
选择 SSE 4.2 实现。
ARM64
SIMD 实现仅在 Rust Nightly 和 Rust 1.59 或更高版本上可用。在 Rust Nightly 中,它现在自动开启。要在 Rust 1.59(可能还包括 1.60)中获取 SIMD 实现,需要启用 crate 功能 aarch64_neon
。对于 Rust Nightly,这将不再需要(但也不会造成伤害)。预计从 Rust 1.61 开始,SIMD 实现将自动开启。
WASM32
对于 wasm32 支持,实现是在编译时根据 simd128
目标功能的是否存在来选择的。使用 RUSTFLAGS="-C target-feature=+simd128"
来启用 WASM SIMD 实现。撰写本文时,WASM 没有通过 WASM 本身检测 SIMD 的方法。尽管这种能力在各种 WASM 主机环境中都可用(例如,浏览器中的 wasm-feature-detect),但在库内部没有可移植的方法来检测这一点。
构建/针对 WASM
更多详情请参阅 此文档。
访问底层功能
如果您想直接调用 SIMD 实现,请使用 public_imp
功能标志。然后,验证实现可通过 simdutf8::{basic, compat}::imp
层次结构访问。那里也有促进流式验证的特质。
优化标志
不要使用 opt-level = "z"
,这会阻止内联并使代码运行非常缓慢。
支持的最低 Rust 版本 (MSRV)
此 crate 的最低支持 Rust 版本是 1.38.0。
基准测试
基准测试使用 criterion 完成,表格使用 critcmp 创建。源代码和数据位于 bench 目录。
命名模式为 id-charset/size。 0-empty 是空的字节切片,x-error/66536 是一个 64KiB 切片,其中第一个字符是无效的 UTF-8。库版本是 simdutf8 v0.1.2 和 simdjson v0.9.2。与 simdjson 比较 时,simdutf8 使用 #inline(never)
编译。
配置
- X86-64:Linux 上的 AMD Ryzen 7 PRO 3700 CPU(Zen2)的 PC,使用 Rust 1.52.0
- Aarch64:搭载 Apple M1 CPU(Apple Silicon)的 Macbook Air,在 macOS 上使用 Rust rustc 1.54.0-nightly(881c1ac40 2021-05-08)。
simdutf8 basic 与 x86-64 上的 std 库(AMD Zen2)对比
在有效的非 ASCII 字符上,simdutf8 比 std 库快 23 倍,在纯 ASCII 上快 4 倍。
simdutf8 basic 与 aarch64(Apple Silicon)上的 std 库对比
simdutf8 在有效的非ASCII字符上的速度比std库快至11倍,在纯ASCII上快至4倍。
simdutf8基本版本与simdjson在x86-64上的比较
simdutf8 在几乎所有输入上比 simdjson 快。
simdutf8基本版本与simdutf8兼容UTF-8在x86-64上的比较
在处理数据时持续检查错误状态会有轻微的性能损失,但提前检测错误对 x-error/66536 基准测试的益处很大。
技术细节
对于小于64字节的输入,验证委托给 core::str::from_utf8()
,除了 simdutf8::{basic, compat}::imp
中的直接访问函数。
SIMD 实现基本上与 simdjson 中的相似,但它对纯 ASCII 情况进行了额外的优化。此外,它在 x86 上使用 AVX 2 进行预取,这在合成基准测试中使一些英特尔 CPU 的性能略有提升。
对于兼容 API,我们需要在每个 64 字节块上检查错误状态向量,而不是仅仅汇总。如果发现错误,将检查前一个块的最后一个字节以查找跨块延续,然后运行 std::str::from_utf8()
以找到错误的精确位置。
注意,所有函数都正确内联到公共接口。
谢谢
- simdjson 的作者提出了高性能 SIMD 实现,特别是感谢 Daniel Lemire 的反馈。这非常有帮助。
- 感谢 simdjson Rust 端口的作者,他们完成了将 C++ 代码移植到 Rust 的繁重工作。
许可证
本代码根据 Apache License 2.0 和 MIT License 双重许可。
它基于 simd-json.rs 代码,这是 simdjson 的 Rust 版本,同样根据 MIT 许可证和 Apache 2.0 许可证双重许可。
simdjson 本身根据 Apache License 2.0 分发。
参考文献
John Keiser,Daniel Lemire,Validating UTF-8 In Less Than One Instruction Per Byte,Software: Practice and Experience 51 (5),2021