You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, when we suspect we're going to need more stack space (e.g. recursive calls) we can use ensure_sufficient_stack to make sure there's enough stack space. This uses stacker (and thus psm) to test if there's less than 100 KiB of stack space left and, if so, allocate 1 MiB and replace the stack with the new allocation. ensure_sufficient_stack takes a closure so it can restore the old stack afterwards.
This requires us to manually account for stack space usage, which can be brittle because the exact sizes of stack frames are not exactly obvious to contributors or reviewers, nor where best to place ensure_sufficient_stack (if they're thinking about it all). It's also not supported on some platforms (e.g. android) or not well supported on others (e.g. on Windows it creates a fiber which is slow).
The traditional way stack growth works is that you set a limit for the stack size (e.g. see std::thread::Builder::stack_size) and the OS will grow the stack up to that limit as needed. Specifically it will reserve virtual memory of the given size but only use a small portion of it initially. When the program touches a guard page just beyond the currently allocated limited, it will allocate at least one more page. Compilers will automatically insert probes that touch pages in advance if the program will need to use that stack space.
This has advantages over ensure_sufficient_stack. It's automatic beyond setting the upper limit (which we already do, just smaller than may be needed without manual stack growth), it's well supported on all host platforms and it's more efficient. By increasing the stack limit and disabling manual stack growth I saw performance improvements of ~1% (source).
So with ensure_sufficient_stack we need to manually handle stack growth, which can be brittle and is slow. Whereas without we need only set an upper limit of virtual memory space we're willing to reserve and the rest is handled automatically by the compiler and OS. The only issue is that any virtual memory reserved by stacks can't be used for other purposes but given modern OSes have terabytes of virtual memory available (if not orders of magnitude more), even a relatively large limit will not be a problem.
Therefore I propose we remove ensure_efficient_stack in favour of increasing the stack limit.
Pros of stacker
Stacker can theoretically make use of all available memory if needed with no arbitrary cutoff (on well supported platforms). Stacker can also free stack memory without needing to exit the thread.
Cons of stacker
Stacker requires us to manually manage stack space to a degree. Making effective use of ensure_sufficient_stack has proved to be difficult to do because it requires constant vigilance so in practice we can still run out of stack space.
Stacker only supports a limited number of platforms so it's a no-op on many. Worse, on platforms that are partially supported (such as android), one failure mode is that the thread only gets 1 MiB of stack even if more stack space is available.
Stacker has a ~1% performance hit on Linux. On Windows it uses fibers so the performance hit is likely higher.
Excessive stack usage is often a bug that should be fixed. Stacker can mask that up to a point but in the worst case runaway recursion can lead to exhausting all memory.
Proposal
Currently, when we suspect we're going to need more stack space (e.g. recursive calls) we can use
ensure_sufficient_stackto make sure there's enough stack space. This usesstacker(and thuspsm) to test if there's less than 100 KiB of stack space left and, if so, allocate 1 MiB and replace the stack with the new allocation.ensure_sufficient_stacktakes a closure so it can restore the old stack afterwards.This requires us to manually account for stack space usage, which can be brittle because the exact sizes of stack frames are not exactly obvious to contributors or reviewers, nor where best to place
ensure_sufficient_stack(if they're thinking about it all). It's also not supported on some platforms (e.g. android) or not well supported on others (e.g. on Windows it creates a fiber which is slow).The traditional way stack growth works is that you set a limit for the stack size (e.g. see std::thread::Builder::stack_size) and the OS will grow the stack up to that limit as needed. Specifically it will reserve virtual memory of the given size but only use a small portion of it initially. When the program touches a guard page just beyond the currently allocated limited, it will allocate at least one more page. Compilers will automatically insert probes that touch pages in advance if the program will need to use that stack space.
This has advantages over
ensure_sufficient_stack. It's automatic beyond setting the upper limit (which we already do, just smaller than may be needed without manual stack growth), it's well supported on all host platforms and it's more efficient. By increasing the stack limit and disabling manual stack growth I saw performance improvements of ~1% (source).So with
ensure_sufficient_stackwe need to manually handle stack growth, which can be brittle and is slow. Whereas without we need only set an upper limit of virtual memory space we're willing to reserve and the rest is handled automatically by the compiler and OS. The only issue is that any virtual memory reserved by stacks can't be used for other purposes but given modern OSes have terabytes of virtual memory available (if not orders of magnitude more), even a relatively large limit will not be a problem.Therefore I propose we remove
ensure_efficient_stackin favour of increasing the stack limit.Pros of stacker
Stacker can theoretically make use of all available memory if needed with no arbitrary cutoff (on well supported platforms). Stacker can also free stack memory without needing to exit the thread.
Cons of stacker
Stacker requires us to manually manage stack space to a degree. Making effective use of
ensure_sufficient_stackhas proved to be difficult to do because it requires constant vigilance so in practice we can still run out of stack space.Stacker only supports a limited number of platforms so it's a no-op on many. Worse, on platforms that are partially supported (such as android), one failure mode is that the thread only gets 1 MiB of stack even if more stack space is available.
Stacker has a ~1% performance hit on Linux. On Windows it uses fibers so the performance hit is likely higher.
Excessive stack usage is often a bug that should be fixed. Stacker can mask that up to a point but in the worst case runaway recursion can lead to exhausting all memory.
Related Links
cranelift-codegenon Termux rust#138889Mentors or Reviewers
Process
The main points of the Major Change Process are as follows:
@rustbot secondor kickoff a team FCP with@rfcbot fcp $RESOLUTION.You can read more about Major Change Proposals on forge.
Note
Concerns (0 active)
back-to-the-pastresolved in this commentManaged by
@rustbot—see help for details.