Memory 如何对函数的内存使用情况进行基准测试?

Memory 如何对函数的内存使用情况进行基准测试?,memory,rust,benchmarking,Memory,Rust,Benchmarking,我注意到Rust的测试有一个基准模式,可以在ns/iter中测量执行时间,但我找不到一种方法来测量内存使用情况 我将如何实施这样一个基准?让我们假设目前我只关心堆内存(尽管堆栈使用也肯定会很有趣) 编辑:我找到了要求完全相同的东西。目前,获取分配信息的唯一方法是alloc::heap::stats_print()方法(在#![feature(alloc)]后面),它调用jemalloc的打印统计() 一旦我了解了输出的含义,我将用进一步的信息更新这个答案 (请注意,我不会接受这个答案,因此如果有

我注意到Rust的测试有一个基准模式,可以在
ns/iter
中测量执行时间,但我找不到一种方法来测量内存使用情况

我将如何实施这样一个基准?让我们假设目前我只关心堆内存(尽管堆栈使用也肯定会很有趣)


编辑:我找到了要求完全相同的东西。

目前,获取分配信息的唯一方法是
alloc::heap::stats_print()方法(在
#![feature(alloc)]
后面),它调用jemalloc的
打印统计()

一旦我了解了输出的含义,我将用进一步的信息更新这个答案


(请注意,我不会接受这个答案,因此如果有人提出更好的解决方案……

就测量数据结构大小而言,这可以通过使用traits和一个小型编译器插件非常容易地完成。尼古拉斯·尼瑟科特(Nicholas Nethercote)在他的文章中演示了它在伺服系统中的工作原理;它归结为向您关心的每种类型添加
#[派生(HeapSizeOf)]
(或偶尔手动实现)。这也是一种允许精确检查内存去向的好方法;然而,它相对来说比较麻烦,因为它首先需要进行更改,而像jemalloc的
print\u stats()
之类的东西则不需要。不过,对于良好和精确的测量,这是一种合理的方法。

您可以使用jemalloc分配器打印分配统计信息。比如说,

Cargo.toml:

[package]
name = "stackoverflow-30869007"
version = "0.1.0"
edition = "2018"

[dependencies]
jemallocator = "0.3"
jemalloc-sys = {version = "0.3", features = ["stats"]}
libc = "0.2"
src/main.rs:

use libc::{c_char, c_void};
use std::ptr::{null, null_mut};

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

extern "C" fn write_cb(_: *mut c_void, message: *const c_char) {
    print!("{}", String::from_utf8_lossy(unsafe {
        std::ffi::CStr::from_ptr(message as *const i8).to_bytes()
    }));
}

fn main() {
    unsafe { jemalloc_sys::malloc_stats_print(Some(write_cb), null_mut(), null()) };
}
在单线程程序中,它应该允许您很好地测量结构占用的内存量。只需在创建结构之前和之后打印统计数据,然后计算差异


还可以使用Valgrind()获取堆配置文件。它的工作原理与任何其他C程序一样。确保在可执行文件中启用了调试符号(例如,使用调试生成或自定义货物配置)。例如,您可以使用来分析生成的堆配置文件

(我在Debian Jessie上验证了这一点,在不同的设置下,您的里程可能会有所不同)

(为了将Rust与Valgrind一起使用,您可能必须切换回系统分配器)

另外,现在也有


jemalloc转储内存配置文件。你可能可以用Rust FFI来做这件事,但我还没有研究过这条路线。

现在有了一个板条箱,它提供了方便的安全类型API。将其添加到您的
货物中。toml

[依赖项]
jemalloc ctl=“0.3”
jemallocator=“0.3”
然后将
jemalloc
配置为be,并使用模块中的方法:


这是:


我怀疑通用方法(在C/C++中也可用)会起作用,但我从未找到一种以通用方式进行测量的细粒度方法:(@Matthieu M.是的,这是可行的,但要求我将所有的基准方法分解成单独的二进制文件,这很麻烦。此外,它可能会给出正确的结果,也可能不会给出正确的结果。要从程序内部执行此操作,我希望您必须等到分配器可插入。然后您必须确保使用的每个堆分配一个提供的分配器,然后实现一个分配器,该分配器跟踪在任何给定时间借出的内存量。我希望valgrind的内存跟踪与jemalloc一起工作……这是一个好的观点,如果我们想测量特定结构的内存使用情况,这是很好的。但是,正如您所说,这是非常侵入性的(尽管对于许多用例来说这可能是可以的),但它不一定能了解全部情况(因为可能存在存储数据的侧通道,例如全局表)@llogiq:正如尼古拉斯所说,处理诸如共享所有权之类的事情仍然是一个悬而未决的问题,但你需要决定如何处理。特质使得决定如何实施这样的事情变得非常容易。你可以用你选择的任何方式来处理这样的事情。这很公平。我真的很喜欢这种方法,它提供了很多控制权。然而,这也是一种挑战有点挑剔,容易出错。因此,虽然在许多情况下这可能是合理的,但这不是我寻求的解决方案。您提到不喜欢另一个答案“因为可能有存储数据的副通道”,但请注意,此答案仅跟踪来自jemalloc的内存,因此,如果您的函数调用使用任何其他分配器的C代码,则此报告中不会包含它。这是一个警告,一旦我们处理非锈代码,基于jemalloc的方法就不那么有用了(或者一旦我们有了可插拔的分配器,另一方面,这可能会使整个问题变得毫无意义)。我对这两个答案都不太满意的另一个原因。
alloc::heap::stats_print()
函数在当前的rust(例如1.42)中不再存在@diralik你知道改用什么吗?@invi使用,这里是我在调试模式下编译的(使用
cargo build
),但我在
massif
转储中看不到行号。我也尝试了
rustc-g
,得到了相同的结果。你知道为什么吗?@antoyo尝试使用
extern-crate-alloc\u-system;
切换到系统分配器。还有cratet,它提供了安全方便的类型化API(例如)是否有一种方法可以打印/可视化堆栈内存状态?@invi可能最好在另一个(新)中询问它问题。但一个可能的解决方案是在
main
函数的开头创建变量,并通过引用此变量获取指针。稍后,当您要测量当前线程的堆栈大小时,可以创建另一个变量并引用第二个变量。堆栈大小是这两个指针之间的差异.
use std::thread;
use std::time::Duration;
use jemalloc_ctl::{stats, epoch};

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn main() {
    loop {
        // many statistics are cached and only updated when the epoch is advanced.
        epoch::advance().unwrap();

        let allocated = stats::allocated::read().unwrap();
        let resident = stats::resident::read().unwrap();
        println!("{} bytes allocated/{} bytes resident", allocated, resident);
        thread::sleep(Duration::from_secs(10));
    }
}