Cargo test --release вызывает переполнение стека. Почему нет грузовой скамьи?

Пытаясь написать оптимизированный алгоритм DSP, я задавался вопросом об относительной скорости между выделением стека и распределением кучи, а также ограничениями размера массивов, выделенных стеком. Я понимаю, что существует ограничение на размер кадра стека, но я не понимаю, почему следующие запуски генерируют кажущиеся реалистичными результаты тестов с cargo bench, но не работают с переполнением стека при запуске с cargo test --release.

#![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]; });
    }
}

person Josh    schedule 22.03.2017    source источник
comment
Вы проверили выданный LLVM IR / сборку?   -  person Matthieu M.    schedule 22.03.2017


Ответы (1)


Чтобы увидеть ситуацию в перспективе, обратите внимание, что размер вашего массива составляет 8 × 2 × 512 × 512 = 4 МБ.

cargo test дает сбой, но cargo bench нет, потому что «тест» вызывает функцию it_works() в новом потоке, а «стенд» вызывает ее в основном потоке.

Размер стека основного потока по умолчанию обычно составляет 8 МБ, поэтому этот массив будет занимать половину доступного стека. Это много, но еще есть место, поэтому тест работает нормально.

Однако размер стека нового потока обычно намного меньше. В Linux это 2 МБ, на других платформах может быть еще меньше. Итак, ваш массив 4 MiB легко переполняет стек потока и вызывает переполнение стека / segfault.

Вы можете увеличить размер стека по умолчанию для новых потоков, установив параметр RUST_MIN_STACK переменная среды.

$ RUST_MIN_STACK=8388608 cargo test 

cargo test выполняет тесты в параллельных потоках, чтобы сократить общее время тестирования, в то время как тесты выполняются последовательно в одном потоке для уменьшения шума.

Из-за ограниченного размера стека размещать этот массив в стеке - плохая идея. Вы должны либо сохранить его в куче (box it), либо как глобальный static mut.

person kennytm    schedule 22.03.2017
comment
Этот ответ абсолютно правдоподобен, но не могли бы вы дать какие-нибудь ссылки? Я немного удивлен, что стенды не запускаются в отдельном потоке, просто с отключенным параллелизмом, потому что потоки также обеспечивают хороший брандмауэр для паники (и я бы ожидал повторного использования кода в любом случае). - person Shepmaster; 22.03.2017
comment
@Shepmaster Я запустил его в LLDB и заметил, что тест выполняется в потоке №1 (основной поток) с флагом --bench и потоке №2 с флагом --test, когда я увеличиваю размер массива до 5120. Соответствующие код вроде бы здесь: - person kennytm; 22.03.2017
comment
Ах да, catch_unwind существует сейчас. Раньше единственный способ поймать панику заключался в отдельной нити. - person Shepmaster; 22.03.2017
comment
Это здорово, спасибо. Я знал, что он большой для стека, а также знал, что тесты выполняются параллельно, хотя я не понимал, что тесты производительности - нет. - person Josh; 22.03.2017