14个版本 (8个重大更改)
0.10.4 | 2024年6月27日 |
---|---|
0.10.2 |
|
0.10.1 |
|
0.9.4 |
|
0.2.3 | 2021年7月13日 |
#23 在 数据结构 中
每月397,191次下载
在 266 个crate中使用了(直接使用37个)
605KB
10K SLoC
zerovec 
基于字切片的任意类型零拷贝向量抽象
zerovec
支持更广泛类型的参与零拷贝反序列化,而不仅仅是&[u8]
和&str
。它与serde
兼容,并配备了过程宏
升级到zerovec
的客户在反序列化只读数据时受益于零堆分配。
此crate有四种主要类型
ZeroVec<'a, T>
(和ZeroSlice<T>
)用于固定宽度的类型,如u32
VarZeroVec<'a, T>
(和VarZeroSlice<T>
)用于可变宽度的类型,如str
ZeroMap<'a, K, V>
将K
映射到V
ZeroMap2d<'a, K0, K1, V>
将对(K0, K1)
的键值对映射到V
前两个旨在作为 Serde 结构体中 Vec<T>
的近似直接替换。第三个和第四个旨在替换 HashMap
或 LiteMap
。当与 Serde derives 一起使用时,请确保将这些类型应用于 #[serde(borrow)]
,就像对 Cow<'a, T>
做的那样。
ZeroVec<'a, T>
、VarZeroVec<'a, T>
、ZeroMap<'a, K, V>
和 ZeroMap2d<'a, K0, K1, V>
都像 Cow<'a, T>
一样,它们抽象化了对借用或所有数据的操作。在从可读格式(如 json
和 xml
)进行反序列化时,通常这些类型将分配并完全拥有它们的数据,而如果从二进制格式(如 bincode
和 postcard
)进行反序列化,这些类型将直接从反序列化缓冲区借用数据,避免分配,并仅执行有效性检查。因此,此 crate 在反序列化时可以非常快(更多信息请参见下方的 下面)。
有关此 crate 的内部工作原理的详细信息,请参阅 设计文档。
Cargo 功能
此 crate 有几个可选的 Cargo 功能
serde
:允许通过serde
对zerovec
的抽象进行序列化和反序列化yoke
:启用从yoke
crate 实现的Yokeable
,这在涉及大量零拷贝反序列化的情况下也非常有用。derive
:通过提供#[make_ule]
和#[make_varule]
proc 宏,使在这些集合中使用自定义类型变得更容易,这些宏为给定的“普通”类型生成适当的ULE
和VarULE
符合的类型。std
:启用了错误类型的std::Error
实现。此 crate 默认为no_std
,并依赖于alloc
。
示例
使用 Bincode 序列化和反序列化 ZeroVec 和 VarZeroVec 结构体
use zerovec::{VarZeroVec, ZeroVec};
// This example requires the "serde" feature
#[derive(serde::Serialize, serde::Deserialize)]
pub struct DataStruct<'data> {
#[serde(borrow)]
nums: ZeroVec<'data, u32>,
#[serde(borrow)]
chars: ZeroVec<'data, char>,
#[serde(borrow)]
strs: VarZeroVec<'data, str>,
}
let data = DataStruct {
nums: ZeroVec::from_slice_or_alloc(&[211, 281, 421, 461]),
chars: ZeroVec::alloc_from_slice(&['ö', '冇', 'म']),
strs: VarZeroVec::from(&["hello", "world"]),
};
let bincode_bytes =
bincode::serialize(&data).expect("Serialization should be successful");
assert_eq!(bincode_bytes.len(), 67);
let deserialized: DataStruct = bincode::deserialize(&bincode_bytes)
.expect("Deserialization should be successful");
assert_eq!(deserialized.nums.first(), Some(211));
assert_eq!(deserialized.chars.get(1), Some('冇'));
assert_eq!(deserialized.strs.get(1), Some("world"));
// The deserialization will not have allocated anything
assert!(!deserialized.nums.is_owned());
在 ZeroVec 中使用自定义类型
use zerovec::{ZeroVec, VarZeroVec, ZeroMap};
use std::borrow::Cow;
use zerovec::ule::encode_varule_to_box;
// custom fixed-size ULE type for ZeroVec
#[zerovec::make_ule(DateULE)]
#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, serde::Serialize, serde::Deserialize)]
struct Date {
y: u64,
m: u8,
d: u8
}
// custom variable sized VarULE type for VarZeroVec
#[zerovec::make_varule(PersonULE)]
#[zerovec::derive(Serialize, Deserialize)] // add Serde impls to PersonULE
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, serde::Serialize, serde::Deserialize)]
struct Person<'a> {
birthday: Date,
favorite_character: char,
#[serde(borrow)]
name: Cow<'a, str>,
}
#[derive(serde::Serialize, serde::Deserialize)]
struct Data<'a> {
#[serde(borrow)]
important_dates: ZeroVec<'a, Date>,
// note: VarZeroVec always must reference the ULE type directly
#[serde(borrow)]
important_people: VarZeroVec<'a, PersonULE>,
#[serde(borrow)]
birthdays_to_people: ZeroMap<'a, Date, PersonULE>
}
let person1 = Person {
birthday: Date { y: 1990, m: 9, d: 7},
favorite_character: 'π',
name: Cow::from("Kate")
};
let person2 = Person {
birthday: Date { y: 1960, m: 5, d: 25},
favorite_character: '冇',
name: Cow::from("Jesse")
};
let important_dates = ZeroVec::alloc_from_slice(&[Date { y: 1943, m: 3, d: 20}, Date { y: 1976, m: 8, d: 2}, Date { y: 1998, m: 2, d: 15}]);
let important_people = VarZeroVec::from(&[&person1, &person2]);
let mut birthdays_to_people: ZeroMap<Date, PersonULE> = ZeroMap::new();
// `.insert_var_v()` is slightly more convenient over `.insert()` for custom ULE types
birthdays_to_people.insert_var_v(&person1.birthday, &person1);
birthdays_to_people.insert_var_v(&person2.birthday, &person2);
let data = Data { important_dates, important_people, birthdays_to_people };
let bincode_bytes = bincode::serialize(&data)
.expect("Serialization should be successful");
assert_eq!(bincode_bytes.len(), 168);
let deserialized: Data = bincode::deserialize(&bincode_bytes)
.expect("Deserialization should be successful");
assert_eq!(deserialized.important_dates.get(0).unwrap().y, 1943);
assert_eq!(&deserialized.important_people.get(1).unwrap().name, "Jesse");
assert_eq!(&deserialized.important_people.get(0).unwrap().name, "Kate");
assert_eq!(&deserialized.birthdays_to_people.get(&person1.birthday).unwrap().name, "Kate");
} // feature = serde and derive
性能
zerovec
设计用于从字节数组中快速反序列化,同时最小化性能退化的内存分配。
x86_64 上的基准测试结果
操作 | Vec<T> |
zerovec |
---|---|---|
反序列化包含 100 个 u32 的 vec |
233.18 纳秒 | 14.120 纳秒 |
计算包含 100 个 u32 的 vec 的和(读取每个元素) |
8.7472 纳秒 | 10.775 纳秒 |
对包含 1000 个 u32 的 vec 进行二分查找 50 次 |
442.80 纳秒 | 472.51 纳秒 |
反序列化包含 100 个字符串的 vec | 7.3740 微秒* | 1.4495 微秒 |
在包含 100 个字符串的 vec 中计数字符(读取每个元素) | 747.50 纳秒 | 955.28 纳秒 |
对 500 个字符串的 vec 进行二分查找 10 次 | 466.09 纳秒 | 790.33 纳秒 |
* 此结果为 Vec<String>
报告。然而,Serde 也支持将序列化到部分零复制的 Vec<&str>
;这给出了 1.8420 微秒,比 Vec<String>
快得多,但比 zerovec
稍慢。
操作 | HashMap<K,V> |
LiteMap<K,V> |
ZeroMap<K,V> |
---|---|---|---|
反序列化小映射 | 2.72 微秒 | 1.28 微秒 | 480 纳秒 |
反序列化大映射 | 50.5 毫秒 | 18.3 毫秒 | 3.74 毫秒 |
从小映射中查找 | 49 纳秒 | 42 纳秒 | 54 纳秒 |
从大映射中查找 | 51 纳秒 | 155 纳秒 | 213 纳秒 |
小 = 16 个元素,大 = 131,072 个元素。映射包含 <String, String>
。
用于生成上述表格的基准测试可以在项目存储库中的 benches
目录中找到。例如,zeromap/deserialize/small
,zeromap/lookup/large
。类型附加用于基线比较,例如 zeromap/lookup/small/hashmap
。
更多信息
有关开发、作者身份、贡献等方面的更多信息,请访问 ICU4X 主页
。