Skip to content

Let the OS handle stack growth #1011

Description

@ChrisDenton

Proposal

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.

Related Links

Mentors or Reviewers

Process

The main points of the Major Change Process are as follows:

  • File an issue describing the proposal.
  • A compiler team member who is knowledgeable in the area can second by writing @rustbot second or kickoff a team FCP with @rfcbot fcp $RESOLUTION.
  • Once an MCP is seconded, the Final Comment Period begins.
    • Final Comment Period lasts for 10 days after all outstanding concerns are solved.
    • Outstanding concerns will block the Final Comment Period from finishing. Once all concerns are resolved, the 10 day countdown is restarted.
    • If no concerns are raised after 10 days since the resolution of the last outstanding concern, the MCP is considered approved.

You can read more about Major Change Proposals on forge.

Note

Concerns (0 active)

Managed by @rustbot—see help for details.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-compilerAdd this label so rfcbot knows to poll the compiler teamfinal-comment-periodThe FCP has started, most (if not all) team members are in agreementmajor-changeA proposal to make a major change to rustc

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions