如何测量 Rust 中的函数堆栈使用情况?
How to measure a functions stack usage in Rust?
有什么方法可以测量一个函数使用了多少堆栈内存?
这个问题不是针对递归函数的;但是我很想知道递归调用的函数需要多少堆栈内存。
我有兴趣优化堆栈内存使用的函数;然而,在不知道编译器已经进行了哪些优化的情况下,这是否在进行真正的改进,这只是猜测。
需要说明的是,这不是关于如何优化以更好地使用堆栈的问题。
那么有没有一些可靠的方法来找出一个函数在 Rust 中使用了多少堆栈内存?
注意其他编译器支持这个,例如GCC有-fstack-usage
。
作为最后的手段,您可以观察堆栈指针(使用内联汇编)并从中推断出结果。这种方法绝对不是您在生产中使用的方法...但它确实有效。
#![feature(asm)]
use std::cell::Cell;
use std::cmp;
use std::usize;
// This global variable tracks the highest point of the stack
thread_local!(static STACK_END: Cell<usize> = Cell::new(usize::MAX));
macro_rules! stack_ptr {
() => ({
// Grab a copy of the stack pointer
let x: usize;
unsafe {
asm!("mov %rsp, [=10=]" : "=r"(x) ::: "volatile");
}
x
})
}
/// Saves the current position of the stack. Any function
/// being profiled must call this macro.
macro_rules! tick {
() => ({
// Save the current stack pointer in STACK_END
let stack_end = stack_ptr!();
STACK_END.with(|c| {
// Since the stack grows down, the "tallest"
// stack must have the least pointer value
let best = cmp::min(c.get(), stack_end);
c.set(best);
});
})
}
/// Runs the given callback, and returns its maximum stack usage
/// as reported by the `tick!()` macro.
fn measure<T, F: FnOnce() -> T>(callback: F) -> (T, usize) {
STACK_END.with(|c| c.set(usize::MAX));
let stack_start = stack_ptr!();
let r = callback();
let stack_end = STACK_END.with(|c| c.get());
if stack_start < stack_end {
panic!("tick!() was never called");
}
(r, stack_start - stack_end)
}
/// Example recursive function
fn fibonacci(n: i64) -> i64 {
tick!();
match n {
0 => 0,
1 => 1,
_ => fibonacci(n-1) + fibonacci(n-2)
}
}
fn main() {
// Stack usage should grow linearly with `i`
for i in 0 .. 10 {
let (result, stack) = measure(|| fibonacci(i));
println!("fibonacci({}) = {}: used {} bytes of stack", i, result, stack);
}
}
截至今天,有一些实验性工具可以估算堆栈使用情况:https://crates.io/crates/cargo-call-stack
它使用实验 -Z emit-stack-sizes
来获取每个函数的堆栈,然后设法提取调用图并从那里生成最坏情况估计
有什么方法可以测量一个函数使用了多少堆栈内存?
这个问题不是针对递归函数的;但是我很想知道递归调用的函数需要多少堆栈内存。
我有兴趣优化堆栈内存使用的函数;然而,在不知道编译器已经进行了哪些优化的情况下,这是否在进行真正的改进,这只是猜测。
需要说明的是,这不是关于如何优化以更好地使用堆栈的问题。
那么有没有一些可靠的方法来找出一个函数在 Rust 中使用了多少堆栈内存?
注意其他编译器支持这个,例如GCC有-fstack-usage
。
作为最后的手段,您可以观察堆栈指针(使用内联汇编)并从中推断出结果。这种方法绝对不是您在生产中使用的方法...但它确实有效。
#![feature(asm)]
use std::cell::Cell;
use std::cmp;
use std::usize;
// This global variable tracks the highest point of the stack
thread_local!(static STACK_END: Cell<usize> = Cell::new(usize::MAX));
macro_rules! stack_ptr {
() => ({
// Grab a copy of the stack pointer
let x: usize;
unsafe {
asm!("mov %rsp, [=10=]" : "=r"(x) ::: "volatile");
}
x
})
}
/// Saves the current position of the stack. Any function
/// being profiled must call this macro.
macro_rules! tick {
() => ({
// Save the current stack pointer in STACK_END
let stack_end = stack_ptr!();
STACK_END.with(|c| {
// Since the stack grows down, the "tallest"
// stack must have the least pointer value
let best = cmp::min(c.get(), stack_end);
c.set(best);
});
})
}
/// Runs the given callback, and returns its maximum stack usage
/// as reported by the `tick!()` macro.
fn measure<T, F: FnOnce() -> T>(callback: F) -> (T, usize) {
STACK_END.with(|c| c.set(usize::MAX));
let stack_start = stack_ptr!();
let r = callback();
let stack_end = STACK_END.with(|c| c.get());
if stack_start < stack_end {
panic!("tick!() was never called");
}
(r, stack_start - stack_end)
}
/// Example recursive function
fn fibonacci(n: i64) -> i64 {
tick!();
match n {
0 => 0,
1 => 1,
_ => fibonacci(n-1) + fibonacci(n-2)
}
}
fn main() {
// Stack usage should grow linearly with `i`
for i in 0 .. 10 {
let (result, stack) = measure(|| fibonacci(i));
println!("fibonacci({}) = {}: used {} bytes of stack", i, result, stack);
}
}
截至今天,有一些实验性工具可以估算堆栈使用情况:https://crates.io/crates/cargo-call-stack
它使用实验 -Z emit-stack-sizes
来获取每个函数的堆栈,然后设法提取调用图并从那里生成最坏情况估计