Rust动态类型大小计算库dyn_size_of的使用,高效获取动态类型内存占用和布局信息

Rust动态类型大小计算库dyn_size_of的使用,高效获取动态类型内存占用和布局信息

dyn_size_of 是由 Piotr Beling 开发的 Rust 库,用于报告变量消耗的近似内存量,包括堆上分配的内存。

简单使用示例

use dyn_size_of::GetSize;

let bs = vec![1u32, 2u32, 3u32].into_boxed_slice();
assert_eq!(bs.size_bytes_dyn(), 3*4);  // 仅计算堆内存
assert_eq!(bs.size_bytes(), 3*4 + std::mem::size_of_val(&bs));  // 计算堆内存和栈内存

为自定义类型实现 GetSize

use dyn_size_of::GetSize;

// 不使用堆内存的类型
struct NoHeapMem {
    a: u32,
    b: u8
}

// 对于不使用堆分配的类型,默认实现即可
impl GetSize for NoHeapMem {}

// 使用堆内存的类型
struct WithHeapMem {
    a: Vec<u32>,
    b: Vec<u8>,
    c: u32
}

// 对于使用堆分配的类型:
impl GetSize for WithHeapMem {
    // 必须实现 size_bytes_dyn 并返回堆内存使用量
    fn size_bytes_dyn(&self) -> usize {
        self.a.size_bytes_dyn() + self.b.size_bytes_dyn()
    }
    // 必须将 USES_DYN_MEM 设置为 true
    const USES_DYN_MEM: bool = true;
}

let s = NoHeapMem { a: 1, b: 2 };
assert_eq!(NoHeapMem::USES_DYN_MEM, false);
assert_eq!(s.size_bytes_dyn(), 0);  // 无堆内存
assert_eq!(s.size_bytes(), std::mem::size_of_val(&s));  // 仅栈内存

let d = WithHeapMem { a: vec![1, 2], b: vec![3, 4], c: 5 };
assert_eq!(WithHeapMem::USES_DYN_MEM, true);
assert_eq!(d.size_bytes_dyn(), 2*4 + 2*1);  // 堆内存计算
assert_eq!(d.size_bytes(), 2*4 + 2*1 + std::mem::size_of_val(&d));  // 堆+栈内存

完整示例代码

下面是一个更完整的示例,展示了如何使用 dyn_size_of 来计算不同类型的内存使用情况:

use dyn_size_of::GetSize;

fn main() {
    // 基本类型示例
    let x = 42u32;
    println!("u32 size: {} bytes (stack only)", x.size_bytes());
    
    // Vec示例
    let vec = vec![1, 2, 3, 4, 5];
    println!("Vec<i32> with 5 elements:");
    println!("  Heap memory: {} bytes", vec.size_bytes_dyn());
    println!("  Total memory: {} bytes", vec.size_bytes());
    
    // String示例
    let s = String::from("Hello, dyn_size_of!");
    println!("String: '{}'", s);
    println!("  Heap memory: {} bytes", s.size_bytes_dyn());
    println!("  Total memory: {} bytes", s.size_bytes());
    
    // 自定义类型示例
    #[derive(Default)]
    struct Person {
        name: String,
        age: u8,
        hobbies: Vec<String>,
    }
    
    impl GetSize for Person {
        fn size_bytes_dyn(&self) -> usize {
            self.name.size_bytes_dyn() + self.hobbies.size_bytes_dyn()
        }
        const USES_DYN_MEM: bool = true;
    }
    
    let person = Person {
        name: String::from("Alice"),
        age: 30,
        hobbies: vec![
            String::from("Programming"),
            String::from("Reading"),
            String::from("Hiking"),
        ],
    };
    
    println!("Person struct:");
    println!("  Heap memory: {} bytes", person.size_bytes_dyn());
    println!("  Total memory: {} bytes", person.size_bytes());
}

安装方法

在您的项目目录中运行以下 Cargo 命令:

cargo add dyn_size_of

或者将以下行添加到您的 Cargo.toml 中:

dyn_size_of = "0.4.4"

许可证

该库采用 MIT 或 Apache-2.0 双重许可证。


1 回复

Rust动态类型大小计算库dyn_size_of的使用

介绍

dyn_size_of是一个Rust库,用于高效获取动态类型的内存占用和布局信息。它特别适用于需要处理动态分发类型(dyn Trait)的场景,能够提供比标准库std::mem::size_of更详细的内存信息。

主要功能

  1. 计算动态类型的大小
  2. 获取类型的内存布局信息
  3. 支持泛型和trait对象
  4. 提供对齐信息

安装

在Cargo.toml中添加依赖:

[dependencies]
dyn_size_of = "0.2"

使用方法

基本用法

use dyn_size_of::GetSize;

#[derive(GetSize)]
struct Example {
    a: u32,
    b: String,
    c: Vec<u8>,
}

fn main() {
    let example = Example {
        a: 42,
        b: "hello".to_string(),
        c: vec![1, 2, 3],
    };
    
    // 获取结构体大小
    println!("Size of Example: {}", example.get_size());
    
    // 获取堆分配的大小
    println!("Heap size of Example: {}", example.get_heap_size());
}

处理trait对象

use dyn_size_of::{GetSize, get_size_of_val_dyn};

trait MyTrait: GetSize {
    fn do_something(&self);
}

#[derive(GetSize)]
struct Impl1 {
    data: Vec<i32>,
}

impl MyTrait for Impl1 {
    fn do_something(&self) {
        println!("Impl1 doing something");
    }
}

#[derive(GetSize)]
struct Impl2 {
    name: String,
    value: f64,
}

impl MyTrait for Impl2 {
    fn do_something(&self) {
        println!("Impl2 doing something");
    }
}

fn main() {
    let impl1: Box<dyn MyTrait> = Box::new(Impl1 {
        data: vec![1, 2, 3],
    });
    
    let impl2: Box<dyn MyTrait> = Box::new(Impl2 {
        name: "test".to_string(),
        value: 3.14,
    });
    
    println!("Size of impl1: {}", get_size_of_val_dyn(&*impl1));
    println!("Size of impl2: {}", get_size_of_val_dyn(&*impl2));
}

获取详细布局信息

use dyn_size_of::{GetSize, get_layout_dyn};

#[derive(GetSize)]
struct Detailed {
    a: u64,
    b: Option<String>,
    c: [u8; 16],
}

fn main() {
    let detailed = Detailed {
        a: 123,
        b: Some("layout".to_string()),
        c: [0; 16],
    };
    
    let layout = get_layout_dyn(&detailed);
    println!("{:#?}", layout);
}

高级用法

自定义类型的大小计算

use dyn_size_of::GetSize;

struct Custom {
    data: Vec<u8>,
    extra: usize,
}

impl GetSize for Custom {
    fn get_size(&self) -> usize {
        std::mem::size_of_val(self) + self.data.capacity()
    }
    
    fn get_heap_size(&self) -> usize {
        self.data.capacity()
    }
}

fn main() {
    let custom = Custom {
        data: vec![0; 100],
        extra: 42,
    };
    
    println!("Total size: {}", custom.get_size());
    println!("Heap size: {}", custom.get_heap_size());
}

忽略某些字段

use dyn_size_of::GetSize;

#[derive(GetSize)]
struct WithIgnore {
    important: Vec<u32>,
    #[ignore_size]
    not_important: std::time::Instant,
}

fn main() {
    let instance = WithIgnore {
        important: vec![1, 2, 3],
        not_important: std::time::Instant::now(),
    };
    
    println!("Size without ignored field: {}", instance.get_size());
}

性能提示

  1. get_size_of_val_dyn比直接调用get_size有额外开销,应避免在性能关键路径频繁使用
  2. 对于已知具体类型的情况,优先使用get_size而不是get_size_of_val_dyn
  3. 考虑缓存计算结果,如果值的大小不会改变

注意事项

  1. 该库无法准确计算包含原始指针的类型的堆内存使用情况
  2. 对于循环引用的数据结构,可能导致无限递归
  3. 计算的大小是近似值,可能与实际内存分配有差异

完整示例demo

以下是一个完整的示例,展示了dyn_size_of库的主要功能:

use dyn_size_of::{GetSize, get_size_of_val_dyn, get_layout_dyn};

// 基本结构体示例
#[derive(GetSize)]
struct Person {
    id: u64,
    name: String,
    age: u8,
    tags: Vec<String>,
}

// 自定义大小的结构体
struct CustomData {
    buffer: Vec<u8>,
    metadata: String,
}

impl GetSize for CustomData {
    fn get_size(&self) -> usize {
        std::mem::size_of_val(self) + self.buffer.capacity() + self.metadata.capacity()
    }
    
    fn get_heap_size(&self) -> usize {
        self.buffer.capacity() + self.metadata.capacity()
    }
}

// 带忽略字段的结构体
#[derive(GetSize)]
struct Config {
    settings: Vec<String>,
    #[ignore_size]
    last_updated: std::time::SystemTime,
}

// Trait对象示例
trait DataSource: GetSize {
    fn fetch(&self) -> Vec<u8>;
}

#[derive(GetSize)]
struct MemorySource {
    data: Vec<u8>,
}

impl DataSource for MemorySource {
    fn fetch(&self) -> Vec<u8> {
        self.data.clone()
    }
}

#[derive(GetSize)]
struct FileSource {
    path: String,
    cache: Vec<u8>,
}

impl DataSource for FileSource {
    fn fetch(&self) -> Vec<u8> {
        self.cache.clone()
    }
}

fn main() {
    // 基本用法
    let person = Person {
        id: 1,
        name: "Alice".to_string(),
        age: 30,
        tags: vec!["admin".to_string(), "user".to_string()],
    };
    println!("Person size: {}", person.get_size());
    println!("Person heap size: {}", person.get_heap_size());
    
    // 自定义大小
    let custom = CustomData {
        buffer: vec![0; 1024],
        metadata: "sample data".to_string(),
    };
    println!("Custom data size: {}", custom.get_size());
    
    // 忽略字段
    let config = Config {
        settings: vec!["dark_mode".to_string(), "notifications".to_string()],
        last_updated: std::time::SystemTime::now(),
    };
    println!("Config size (ignoring timestamp): {}", config.get_size());
    
    // Trait对象
    let memory_source: Box<dyn DataSource> = Box::new(MemorySource {
        data: vec![1, 2, 3, 4, 5],
    });
    let file_source: Box<dyn DataSource> = Box::new(FileSource {
        path: "/data/sample.bin".to_string(),
        cache: vec![0; 512],
    });
    
    println!("Memory source size: {}", get_size_of_val_dyn(&*memory_source));
    println!("File source size: {}", get_size_of_val_dyn(&*file_source));
    
    // 获取布局信息
    let layout = get_layout_dyn(&person);
    println!("Person layout: {:#?}", layout);
    
    // 性能提示:缓存已知类型的大小
    let person_size = person.get_size();
    println!("Cached person size: {}", person_size);
}
回到顶部