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 编码

Download history 375673/week @ 2024-03-14 368304/week @ 2024-03-21 362990/week @ 2024-03-28 358663/week @ 2024-04-04 384484/week @ 2024-04-11 364366/week @ 2024-04-18 335262/week @ 2024-04-25 323637/week @ 2024-05-02 346176/week @ 2024-05-09 341312/week @ 2024-05-16 350005/week @ 2024-05-23 372949/week @ 2024-05-30 371719/week @ 2024-06-06 362805/week @ 2024-06-13 365696/week @ 2024-06-20 294600/week @ 2024-06-27

1,459,932 每月下载量
817 个crate中使用 (60直接使用)

MIT/Apache

97KB
2K SLoC

CI crates.io docs.rs

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)对比

image 在有效的非 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.0MIT 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

无运行时依赖

特性