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