14个版本 (8个重大更改)

0.10.4 2024年6月27日
0.10.2 2024年5月28日
0.10.1 2023年11月16日
0.9.4 2023年4月13日
0.2.3 2021年7月13日

#23数据结构

Download history 32483/week @ 2024-05-04 30789/week @ 2024-05-11 32409/week @ 2024-05-18 32434/week @ 2024-05-25 33074/week @ 2024-06-01 487343/week @ 2024-06-08 453417/week @ 2024-06-15 241394/week @ 2024-06-22 131128/week @ 2024-06-29 102854/week @ 2024-07-06 98035/week @ 2024-07-13 95758/week @ 2024-07-20 93201/week @ 2024-07-27 105376/week @ 2024-08-03 101456/week @ 2024-08-10 81564/week @ 2024-08-17

每月397,191次下载
266 个crate中使用了(直接使用37个)

Unicode-3.0

605KB
10K SLoC

zerovec crates.io

基于字切片的任意类型零拷贝向量抽象

zerovec支持更广泛类型的参与零拷贝反序列化,而不仅仅是&[u8]&str。它与serde兼容,并配备了过程宏

升级到zerovec的客户在反序列化只读数据时受益于零堆分配。

此crate有四种主要类型

前两个旨在作为 Serde 结构体中 Vec<T> 的近似直接替换。第三个和第四个旨在替换 HashMapLiteMap。当与 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> 一样,它们抽象化了对借用或所有数据的操作。在从可读格式(如 jsonxml)进行反序列化时,通常这些类型将分配并完全拥有它们的数据,而如果从二进制格式(如 bincodepostcard)进行反序列化,这些类型将直接从反序列化缓冲区借用数据,避免分配,并仅执行有效性检查。因此,此 crate 在反序列化时可以非常快(更多信息请参见下方的 下面)。

有关此 crate 的内部工作原理的详细信息,请参阅 设计文档

Cargo 功能

此 crate 有几个可选的 Cargo 功能

  • serde:允许通过 serdezerovec 的抽象进行序列化和反序列化
  • yoke:启用从 yoke crate 实现的 Yokeable,这在涉及大量零拷贝反序列化的情况下也非常有用。
  • derive:通过提供 #[make_ule]#[make_varule] proc 宏,使在这些集合中使用自定义类型变得更容易,这些宏为给定的“普通”类型生成适当的 ULEVarULE 符合的类型。
  • 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/smallzeromap/lookup/large。类型附加用于基线比较,例如 zeromap/lookup/small/hashmap

更多信息

有关开发、作者身份、贡献等方面的更多信息,请访问 ICU4X 主页

依赖项