Is there a way I can measure how much stack memory a function uses?
This question isn't specific to recursive functions; however I was interested to know how much stack memory a function called recursively would take.
I was interested to optimize the function for stack memory usage; however, without knowing what optimizations the compiler is already making, it's just guess-work if this is making real improvements or not.
To be clear, this is not a question about how to optimize for better stack usage.
So is there some reliable way to find out how much stack memory a function uses in Rust?
Note that other compilers support this, GCC has -fstack-usage
for example.
The most common way to determine the deepest stack usage is to initialize the stack memory with some known but unusual value, then periodically (or at the end of a big test run) see where that pattern stops. This is exactly how the IAR IDE determines the amount of stack used.
Memory management The stack is very fast, and is where memory is allocated in Rust by default. But the allocation is local to a function call, and is limited in size. The heap, on the other hand, is slower, and is explicitly allocated by your program. But it's effectively unlimited in size, and is globally accessible.
Stack memory is a memory usage mechanism that allows the system memory to be used as temporary data storage that behaves as a first-in-last-out buffer. One of the essential elements of stack memory operation is a register called the Stack Pointer.
Rust's default item stack limit is 100; however, some users may wish to increase this value, for example: modded servers who have increased their Gather Rates. In order to increase your Rust server's item stack limit, you will need to take a few steps.
As a last resort, you can observe the stack pointer (using inline assembly) and infer the result from that. This method is definitely not something you'd use in production... but it works.
#![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, $0" : "=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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With