#size #memory-size #heap-memory #heap #memory #ram #cache

get-size

确定对象在RAM中占用的字节数

5个版本

0.1.4 2023年6月23日
0.1.3 2023年3月6日
0.1.2 2023年1月3日
0.1.1 2022年2月27日
0.1.0 2022年2月27日

#14缓存

Download history 38505/week @ 2024-03-14 40289/week @ 2024-03-21 49361/week @ 2024-03-28 49947/week @ 2024-04-04 58418/week @ 2024-04-11 55735/week @ 2024-04-18 54451/week @ 2024-04-25 60220/week @ 2024-05-02 58378/week @ 2024-05-09 61205/week @ 2024-05-16 68708/week @ 2024-05-23 79284/week @ 2024-05-30 77363/week @ 2024-06-06 72279/week @ 2024-06-13 63583/week @ 2024-06-20 73796/week @ 2024-06-27

307,512 每月下载量
用于 46 个crates(27 个直接使用)

MIT 协议

32KB
542 代码行

get-size

Crates.io docs.rs MIT licensed

确定对象在RAM中占用的字节数。

可以使用 GetSize 特性来确定对象在栈和堆中的大小。标准库提供的 size_of 函数已可用于确定栈中对象的大小,但许多应用程序(例如,用于缓存)还需要知道堆中占用的字节数,为此该库提供了一个适当的特性。

示例

我们使用 GetSize 来确定 String 和字节 Vec 占用的字节数。请注意,Vec 已经分配了 1024 字节的容量,因此即使当前只使用 1 字节,它也能正确显示 1024 的堆大小。

use get_size::GetSize;

fn main() {
  let value = String::from("Hello World!");

  assert_eq!(String::get_stack_size(), std::mem::size_of::<String>());
  assert_eq!(value.get_heap_size(), 12);

  assert_eq!(value.get_size(), std::mem::size_of::<String>() + 12);


  let mut buffer = Vec::with_capacity(1024); // 1KB allocated on the heap.
  buffer.push(1u8); // 1 byte in use.

  assert_eq!(buffer.len(), 1);
  assert_eq!(buffer.get_heap_size(), 1024);
}

基于所有权的会计

该库遵循的想法是,只有属于某个对象的字节应该被计入,而不是属于不同对象的只被借用字节的会计。这意味着特别是被指针引用的对象被忽略。

示例
use get_size::GetSize;

#[derive(GetSize)]
struct Test<'a> {
  value: &'a String,
}

fn main() {
  let value = String::from("hello");

  // This string occupies 5 bytes at the heap, but a pointer is treated as not occupying
  // anything at the heap.
  assert_eq!(value.get_heap_size(), 5);
  assert_eq!(GetSize::get_heap_size(&&value), 0); // Fully qualified syntax

  // WARNING: Duo to rust's automatic dereferencing, a simple pointer will be dereferenced
  // to the original value, causing the borrowed bytes to be accounted for too.
  assert_eq!((&value).get_heap_size(), 5);
  // The above gets rewritten by to compiler into:
  // assert_eq!(value.get_heap_size(), 5);

  // Our derive macro uses fully qualified syntax, so auto-dereferencing does
  // not occour.
  let value = Test {
    value: &value,
  };

  // The String is now only borrowed, leading to its heap bytes not being
  // accounted for.
  assert_eq!(value.get_heap_size(), 0);
}

另一方面,作为共享所有权的引用被处理为所有值。确保它们占用的字节数在您的应用程序中不被重复计算是您的责任。可能有助于 ignore 属性,见下文

示例
use std::sync::Arc;
use get_size::GetSize;

fn main() {
  let value = String::from("hello");
  assert_eq!(value.get_heap_size(), 5);

  // From a technical point of view, Arcs own the data they reference.
  // Given so their heap data gets accounted for too.
  // Note that an Arc does store the String's stack bytes also inside the heap.
  let value = Arc::new(value);
  assert_eq!(value.get_heap_size(), std::mem::size_of::<String>() + 5);
}

如何实现

GetSize 特性已经为大多数由标准库定义的对象实现,例如 VecHashMapString 以及所有原始值,例如 u8i32 等。

除非你需要一个复杂的复杂数据结构且需要手动实现,否则你可以轻松地为你的结构和枚举推导出 GetSize。推导的实现将简单地通过调用结构或枚举变体内部所有值上的 get_heap_size 来实现 get_heap_size 并返回它们的总和。

首先需要启用 derive 功能,该功能默认是禁用的。在你的 cargo.toml 中添加以下内容

get-size = { version = "^0.1", features = ["derive"] }

请注意,推导宏 不支持联合。你必须为它们手动实现。

示例

为结构推导 GetSize

use get_size::GetSize;

#[derive(GetSize)]
pub struct OwnStruct {
    value1: String,
    value2: u64,
}

fn main() {
    let test = OwnStruct {
        value1: "Hello".into(),
        value2: 123,
    };

    assert_eq!(test.get_heap_size(), 5);
}

为枚举推导 GetSize

use get_size::GetSize;

#[derive(GetSize)]
pub enum TestEnum {
    Variant1(u8, u16, u32),
    Variant2(String),
    Variant3,
    Variant4{x: String, y: String},
}

#[derive(GetSize)]
pub enum TestEnumNumber {
    Zero = 0,
    One = 1,
    Two = 2,
}

fn main() {
    let test = TestEnum::Variant1(1, 2, 3);
    assert_eq!(test.get_heap_size(), 0);

    let test = TestEnum::Variant2("Hello".into());
    assert_eq!(test.get_heap_size(), 5);

    let test = TestEnum::Variant3;
    assert_eq!(test.get_heap_size(), 0);

    let test = TestEnum::Variant4{x: "Hello".into(), y: "world".into()};
    assert_eq!(test.get_heap_size(), 5 + 5);

    let test = TestEnumNumber::One;
    assert_eq!(test.get_heap_size(), 0);
}

推导宏还与泛型一起工作。默认情况下,生成的特质实现将要求所有泛型类型自身实现 GetSize,但这一点 可以更改

use get_size::GetSize;

#[derive(GetSize)]
struct TestStructGenerics<A, B> {
    value1: A,
    value2: B,
}

#[derive(GetSize)]
enum TestEnumGenerics<A, B> {
  Variant1(A),
  Variant2(B),
}

fn main() {
    let test: TestStructGenerics<String, u64> = TestStructGenerics {
        value1: "Hello".into(),
        value2: 123,
    };

    assert_eq!(test.get_heap_size(), 5);

    let test = String::from("Hello");
    let test: TestEnumGenerics<String, u64> = TestEnumGenerics::Variant1(test);

    assert_eq!(test.get_heap_size(), 5);

    let test: TestEnumGenerics<String, u64> = TestEnumGenerics::Variant2(100);

    assert_eq!(test.get_heap_size(), 0);
}

处理未实现 GetSize 的外部类型

如果您的数据结构中包含的类型都实现了 GetSize,则推导 GetSize 是直接了当的,但情况可能并非总是如此。因此,推导宏提供了一些助手以帮助您处理这种情况。

请注意,目前这些助手仅适用于常规结构,即它们不支持元组结构或枚举。

忽略某些值

您可以通过在它们上添加 ignore 属性来告诉推导宏忽略某些结构字段。然后,生成的 get_heap_size 实现将简单地跳过此字段。

示例

此辅助器的典型用途是如果您使用共享所有权并且不希望您的数据被计算两次。

use std::sync::Arc;
use get_size::GetSize;

#[derive(GetSize)]
struct PrimaryStore {
  id: u64,
  shared_data: Arc<Vec<u8>>,
}

#[derive(GetSize)]
struct SecondaryStore {
  id: u64,
  #[get_size(ignore)]
  shared_data: Arc<Vec<u8>>,
}

fn main() {
  let shared_data = Arc::new(Vec::with_capacity(1024));

  let primary_data = PrimaryStore {
    id: 1,
    shared_data: Arc::clone(&shared_data),
  };

  let secondary_data = SecondaryStore {
    id: 2,
    shared_data,
  };

  // Note that Arc does also store the Vec's stack data on the heap.
  assert_eq!(primary_data.get_heap_size(), Vec::<u8>::get_stack_size() + 1024);
  assert_eq!(secondary_data.get_heap_size(), 0);
}
示例

但您也可以将其用作临时解决方案,如果某个结构字段的类型没有实现 GetSize

但请注意,这将导致返回错误结果的实现,除非该类型的堆大小确实始终为零且可以忽略。因此,建议使用以下两种辅助选项之一。

use get_size::GetSize;

// Does not implement GetSize!
struct TestStructNoGetSize {
    value: String,
}

// Implements GetSize, even through one field's type does not implement it.
#[derive(GetSize)]
struct TestStruct {
  name: String,
  #[get_size(ignore)]
  ignored_value: TestStructNoGetSize,
}

fn main() {
  let ignored_value = TestStructNoGetSize {
    value: "Hello world!".into(),
  };

  let test = TestStruct {
    name: "Adam".into(),
    ignored_value,
  };

  // Note that the result is lower then it should be.
  assert_eq!(test.get_heap_size(), 4);
}

返回固定值

在某些情况下,您可能正在处理分配固定数量字节的堆的外部类型。在这种情况下,您可以使用 size 属性始终使用固定值计算给定的字段。

use get_size::GetSize;

#[derive(GetSize)]
struct TestStruct {
  id: u64,
  #[get_size(size = 1024)]
  buffer: Buffer1024, // Always allocates exactly 1KB at the heap.
}

fn main() {
  let test = TestStruct {
    id: 1,
    buffer: Buffer1024::new(),
  };

  assert_eq!(test.get_heap_size(), 1024);
}

使用辅助函数

在某些情况下,您可能正在处理一个外部数据结构,您知道如何使用其公共方法计算其堆大小。在这种情况下,您可以使用新类型模式直接为它实现 GetSize,或者您可以使用 size_fn 属性,该属性将调用给定的函数以计算字段的堆大小。

后者特别有用,如果您可以使用某个特质为多个类型计算堆大小。

请注意,与在其他crate中不同,要调用的函数的名称 不是 用双引号 ("") 封装,而是直接给出。

use get_size::GetSize;

#[derive(GetSize)]
struct TestStruct {
  id: u64,
  #[get_size(size_fn = vec_alike_helper)]
  buffer: ExternalVecAlike<u8>,
}

// NOTE: We assume that slice.len()==slice.capacity()
fn vec_alike_helper<V, T>(slice: &V) -> usize
where
  V: AsRef<[T]>,
{
  std::mem::size_of::<T>() * slice.as_ref().len()
}

fn main() {
  let buffer = vec![0u8; 512];
  let buffer: ExternalVecAlike<u8> = buffer.into();

  let test = TestStruct {
    id: 1,
    buffer,
  };

  assert_eq!(test.get_heap_size(), 512);
}

忽略某些泛型类型

如果你的结构体使用了泛型,但它们存储的字段被忽略或由辅助函数处理,因为泛型没有实现 GetSize,你将必须用特殊的结构体级别 ignore 属性标记这些泛型。否则,派生的 GetSize 实现仍然需要这些泛型实现 GetSize,即使实际上并不需要。

use get_size::GetSize;

#[derive(GetSize)]
#[get_size(ignore(B, C, D))]
struct TestStructHelpers<A, B, C, D> {
    value1: A,
    #[get_size(size = 100)]
    value2: B,
    #[get_size(size_fn = get_size_helper)]
    value3: C,
    #[get_size(ignore)]
    value4: D,
}

// Does not implement GetSize
struct NoGS {}

fn get_size_helper<C>(_value: &C) -> usize {
    50
}

fn main() {
    let test: TestStructHelpers<String, NoGS, NoGS, u64> = TestStructHelpers {
        value1: "Hello".into(),
        value2: NoGS {},
        value3: NoGS {},
        value4: 123,
    };

    assert_eq!(test.get_heap_size(), 5 + 100 + 50);
}

许可证

此库遵循 MIT 许可证

贡献

除非你明确声明,否则,你提交给包含在此库中的任何有意贡献,都将按 MIT 许可证许可,不附加任何额外条款或条件。

依赖关系

~185KB