You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2.3 KiB

线程类型导致的栈溢出

在Rust中我们不太容易遇到栈溢出因为默认栈还挺大的而且大的数据往往存在堆上(动态增长),但是一旦遇到该如何处理?先来看段代码:

#![feature(test)]
extern crate test;

#[cfg(test)]
mod tests {
    use test::Bencher;

    #[bench]
    fn it_works(b: &mut Bencher) {
        b.iter(|| { let stack = [[[0.0; 2]; 512]; 512]; });
    }
}

以上代码是一个测试模块,它在堆上生成了一个数组stack,初步看起来数组挺大的,先尝试运行下cargo test:

你很可能会遇到#![feature(test)]错误,因为该特性目前只存在Rust Nightly版本上,具体解决方法见Rust语言圣经

running 1 test

thread 'tests::it_works' has overflowed its stack
fatal runtime error: stack overflow

Bang很不幸遇到了百年一遇的栈溢出错误再来试试cargo bench,竟然通过了测试,这是什么原因?为何cargo testcargo bench拥有完全不同的行为这就要从Rust的栈原理讲起。

首先看看stack数组,它的大小是8 × 2 × 512 × 512 = 4 MiB,嗯,很大,崩溃也正常(读者说,正常,只是作者你不太正常。。).

其次,cargo testcargo bench,前者运行在一个新创建的线程上,而后者运行在main线程上.

最后,main线程由于是老大,所以资源比较多,拥有令其它兄弟艳羡不已的8MB栈大小,而其它新线程只有区区2MB栈大小(取决于操作系统,linux2MB,其它的可能更小),再对比我们的stack大小,不崩溃就奇怪了。

因此,你现在明白,为何cargo test不能运行,而cargo bench却可以欢快运行。

如果实在想要增大栈的默认大小,以通过该测试,你可以这样运行:RUST_MIN_STACK=8388608 cargo test,结果如下:

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Bingo, 成功了,最后再补充点测试的背景知识:

cargo test为何使用新线程因为它需要并行的运行测试用例与之相反cargo bench只需要顺序的执行因此main线程足矣