Skip to content

Commit 08dbc14

Browse files
author
Julian Wollersberger
committed
To avoid monomorphizing psm::on_stack::with_on_stack 1500 times in rustc, convert the callback passed to grow _grow to dyn FnMut().
This should improve rustc's compile time a bit, and surprisingly also improves runtime in some cases. https://perf.rust-lang.org/compare.html?start=7402a394471a6738a40fea7d4f1891666e5a80c5&end=f242334c1e2e719cf1cba923923ad8ec62affb71
1 parent 7a1d421 commit 08dbc14

File tree

1 file changed

+25
-9
lines changed

1 file changed

+25
-9
lines changed

src/lib.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ use std::cell::Cell;
4747
pub fn maybe_grow<R, F: FnOnce() -> R>(red_zone: usize, stack_size: usize, callback: F) -> R {
4848
// if we can't guess the remaining stack (unsupported on some platforms) we immediately grow
4949
// the stack and then cache the new stack size (which we do know now because we allocated it.
50-
let enough_space = remaining_stack().map_or(false, |remaining| remaining >= red_zone);
50+
let enough_space = match remaining_stack() {
51+
Some(remaining) => remaining >= red_zone,
52+
None => false,
53+
};
5154
if enough_space {
5255
callback()
5356
} else {
@@ -59,11 +62,24 @@ pub fn maybe_grow<R, F: FnOnce() -> R>(red_zone: usize, stack_size: usize, callb
5962
/// The closure will still be on the same thread as the caller of `grow`.
6063
/// This will allocate a new stack with at least `stack_size` bytes.
6164
pub fn grow<R, F: FnOnce() -> R>(stack_size: usize, callback: F) -> R {
65+
// To avoid monomorphizing `_grow()` and everything it calls,
66+
// we convert the generic callback to a dynamic one.
67+
let mut opt_callback = Some(callback);
6268
let mut ret = None;
6369
let ret_ref = &mut ret;
64-
_grow(stack_size, move || {
65-
*ret_ref = Some(callback());
66-
});
70+
71+
// This wrapper around `callback` achieves two things:
72+
// * It converts the `impl FnOnce` to a `dyn FnMut`.
73+
// `dyn` because we want it to not be generic, and
74+
// `FnMut` because we can't pass a `dyn FnOnce` around without boxing it.
75+
// * It eliminates the generic return value, by writing it to the stack of this function.
76+
// Otherwise the closure would have to return an unsized value, which isn't possible.
77+
let dyn_callback: &mut dyn FnMut() = &mut || {
78+
let taken_callback = opt_callback.take().unwrap();
79+
*ret_ref = Some(taken_callback());
80+
};
81+
82+
_grow(stack_size, dyn_callback);
6783
ret.unwrap()
6884
}
6985

@@ -201,7 +217,7 @@ psm_stack_manipulation! {
201217
}
202218
}
203219

204-
fn _grow<F: FnOnce()>(stack_size: usize, callback: F) {
220+
fn _grow(stack_size: usize, callback: &mut dyn FnMut()) {
205221
// Calculate a number of pages we want to allocate for the new stack.
206222
// For maximum portability we want to produce a stack that is aligned to a page and has
207223
// a size that’s a multiple of page size. Furthermore we want to allocate two extras pages
@@ -249,7 +265,7 @@ psm_stack_manipulation! {
249265

250266
no {
251267
#[cfg(not(windows))]
252-
fn _grow<F: FnOnce()>(stack_size: usize, callback: F) {
268+
fn _grow(stack_size: usize, callback: &mut dyn FnMut()) {
253269
drop(stack_size);
254270
callback();
255271
}
@@ -298,7 +314,7 @@ cfg_if! {
298314
return;
299315
}
300316

301-
fn _grow<F: FnOnce()>(stack_size: usize, callback: F) {
317+
fn _grow(stack_size: usize, callback: &mut dyn FnMut()) {
302318
// Fibers (or stackful coroutines) is the only official way to create new stacks on the
303319
// same thread on Windows. So in order to extend the stack we create fiber and switch
304320
// to it so we can use it's stack. After running `callback` within our fiber, we switch
@@ -329,8 +345,8 @@ cfg_if! {
329345

330346
let fiber = CreateFiber(
331347
stack_size as SIZE_T,
332-
Some(fiber_proc::<F>),
333-
&mut data as *mut FiberInfo<F> as *mut _,
348+
Some(fiber_proc::<&mut dyn FnMut()>),
349+
&mut data as *mut FiberInfo<&mut dyn FnMut()> as *mut _,
334350
);
335351
if fiber.is_null() {
336352
panic!("unable to allocate fiber: {}", io::Error::last_os_error());

0 commit comments

Comments
 (0)