17 个版本

0.2.15 2023 年 9 月 11 日
0.2.14 2023 年 3 月 8 日
0.2.13 2022 年 12 月 27 日
0.2.11 2022 年 11 月 4 日
0.2.3 2020 年 11 月 18 日

#129内存管理

Download history 1726/week @ 2024-04-16 2239/week @ 2024-04-23 2016/week @ 2024-04-30 2125/week @ 2024-05-07 2048/week @ 2024-05-14 2162/week @ 2024-05-21 1958/week @ 2024-05-28 1949/week @ 2024-06-04 1797/week @ 2024-06-11 1702/week @ 2024-06-18 2683/week @ 2024-06-25 1997/week @ 2024-07-02 3529/week @ 2024-07-09 3573/week @ 2024-07-16 3609/week @ 2024-07-23 3175/week @ 2024-07-30

14,355 每月下载量
31 个 crates 中使用 (12 直接)

MIT/Apache

45KB
973 代码行

堆数据估算器。

datasize crate 允许估算值的堆内存使用量。它通过提供或推导 DataSize 特性的实现来完成,该特性知道如何为许多 std 类型和原语计算大小。

目标是得到内存使用的合理近似,特别是对于像 Vec 这样的可变大小类型。虽然在某些情况下偏离几个字节是可以接受的,但任何用户都应该能够通过查看报告的数字轻松判断其内存是线性增长还是对数增长。

该 crate 不考虑对齐或内存布局,也不考虑分配器的异常行为或优化。它完全依赖于类型内部的数据,因此得名。

通用用法

对于实现了 DataSize 的任何类型,可以使用 data_size 便利函数来猜测其堆分配的大小

use datasize::data_size;

let data: Vec<u64> = vec![1, 2, 3];
#[cfg(feature = "std")]
assert_eq!(data_size(&data), 24);

实现该特质的类型还提供两个附加常量,IS_DYNAMICSTATIC_HEAP_SIZE

IS_DYNAMIC 表示值的尺寸是否可以随时间变化

use datasize::DataSize;

#[cfg(feature = "std")]
// A `Vec` of any kind may have elements added or removed, so it changes size.
assert!(Vec::<u64>::IS_DYNAMIC);

// The elements of type `u64` in it are not dynamic. This allows the implementation to
// simply estimate the size as number_of_elements * size_of::<u64>.
assert!(!u64::IS_DYNAMIC);

此外,STATIC_HEAP_SIZE 表示类型将始终使用的堆内存量。一个很好的例子是 Box<u64> -- 它将始终使用 8 字节的堆内存,但大小不会改变

use datasize::DataSize;

#[cfg(feature = "std")]
assert_eq!(Box::<u64>::STATIC_HEAP_SIZE, 8);
#[cfg(feature = "std")]
assert!(!Box::<u64>::IS_DYNAMIC);

覆盖单个字段的推导数据大小计算。

在struct中(但不包括enum!)可以覆盖单个字段的堆大小计算,这在处理第三方crate时很有用,这些crate的字段没有实现DataSize,只需简单地用#[data_size(with = ...)]注解并指向一个Fn(T) -> usize函数

use datasize::DataSize;

// Let's pretend this type is from a foreign crate.
struct ThirdPartyType;

fn estimate_third_party_type(value: &Vec<ThirdPartyType>) -> usize {
    // We assume every item is 512 bytes in heap size.
    value.len() * 512
}

#[cfg(feature = "std")]
#[derive(DataSize)]
struct MyStruct {
    items: Vec<u32>,
    #[data_size(with = estimate_third_party_type)]
    other_stuff: Vec<ThirdPartyType>,
}

这会自动将整个struct标记为始终是动态的,因此每次在计算MyStruct的大小时都会调用自定义估计函数。

为自定义类型实现DataSize

可以手动为自定义类型实现DataSize trait

struct MyType {
    items: Vec<i64>,
    flag: bool,
    counter: Box<u64>,
}

#[cfg(feature = "std")]
impl DataSize for MyType {
    // `MyType` contains a `Vec`, so `IS_DYNAMIC` is set to true.
    const IS_DYNAMIC: bool = true;

    // The only always present heap item is the `counter` value, which is 8 bytes.
    const STATIC_HEAP_SIZE: usize = 8;

    #[inline]
    fn estimate_heap_size(&self) -> usize {
        // We can be lazy here and delegate to all the existing implementations:
        data_size(&self.items) + data_size(&self.flag) + data_size(&self.counter)
    }
}

let my_data = MyType {
    items: vec![1, 2, 3],
    flag: true,
    counter: Box::new(42),
};

#[cfg(feature = "std")]
// Three i64 and one u64 on the heap sum up to 32 bytes:
assert_eq!(data_size(&my_data), 32);

由于为struct类型实现这个 trait 很繁琐且重复,crate提供了一个方便的DataSize

// Equivalent to the manual implementation above:
#[cfg(feature = "std")]
#[derive(DataSize)]
struct MyType {
    items: Vec<i64>,
    flag: bool,
    counter: Box<u64>,
}

有关详细信息,请参阅datasize_derive crate中的DataSize宏文档。

性能考虑

确定数据的完整大小可能相当昂贵,尤其是在使用了多层嵌套的动态类型时。crate使用IS_DYNAMICSTATIC_HEAP_SIZE在可能的情况下进行优化,因此在许多情况下不需要逐个检查向量的每个元素。

然而,如果包含的类型是动态的,则必须(并且将)检查每个元素,因此在性能问题时请记住这一点。

处理引用、Arc和类似类型

任何引用都将被计为具有0的数据大小,因为它不拥有值。还有一些特殊的类似引用类型,如Arc,下面将进行讨论。

ArcRc

目前不支持Arc。计划中的开发是允许用户将Arc的实例标记为“主要”,并计算其堆内存使用情况,但目前尚未实现。

任何Arc都将估计为具有0的堆大小,以避免循环导致无限循环。

Rc类型以相同的方式处理。

其他类型

一些外部crate的类型可以通过功能标志使用。

  • fake_clock-types:支持fake_instant::FakeClock类型。
  • futures-types:来自futures crate的一些类型。
  • smallvec-types:支持smallvec::SmallVec类型。
  • tokio-types:来自tokio crate的一些类型。

no_std支持

虽然由于没有std或至少alloc,在大多数情况下不会存在堆,但crate支持no_std环境。通过禁用“std”功能(通过禁用默认功能)将生成不依赖于标准库的crate版本。这可以用来为类型推导DataSize trait,即使它们的堆大小通常是0。

数组和const泛型

默认情况下,此crate需要至少Rust版本1.51.0,以便泛型实现[T; N]数组的DataSize。此实现由"const-generics"功能标志提供,该标志默认启用。要使用较旧的Rust版本,您可以在Cargo.toml中指定default-features = falsefeatures = ["std"]用于datasize

当禁用const-generics功能标志时,将为小尺寸的数组以及与2的幂相关的一些较大尺寸的数组提供DataSize实现。

已知问题

当前derive宏不支持具有内联类型边界的泛型结构体,例如。

struct Foo<T: Copy> { ... }

可以通过使用等效的where子句来解决此问题。

struct Foo<T>
where T: Copy
{ ... }

依赖关系

~1.2–2.5MB
~51K SLoC