Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: allow no-escape slices of unknown capacity to be allocated to the stack more aggressively #58215

Open
fredbi opened this issue Feb 1, 2023 · 5 comments
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Milestone

Comments

@fredbi
Copy link

fredbi commented Feb 1, 2023

Context

Whenever a slice is allocated with a variable capacity (e.g. make([]int, 0, n)), the compiler concludes it escapes to the heap,
even though the slice does not actually escape.

Examples:

func sliceAllocInt(n int) int {
	index := make([]int, 0) // does not escape, even though the append makes it allocate on the heap

	for i := 0; i < n; i++ {
		index = append(index, i)
	}

	return len(index)
}

func sliceAllocMakeInt(n int) int {
	index := make([]int, n) // escapes, since n is unknown

	for i := 0; i < n; i++ {
		index[i] = i
	}

	return len(index)
}

func sliceAllocMakeConstInt(_ int) int {
	const size = 32
	index := make([]int, size) // does not escape: size is known at build time: correctly allocated to the stack

	for i := 0; i < size; i++ {
		index[i] = i
	}

	return len(index)
}

Notice that this contrasts with escaping conclusions about similar constructs with maps:

  • snippet (i) no escape, but will allocate on the heap because of the need to grow
  • snippet (ii) escape (even if it actually doesn't), because the capacity to allocate is not known at build time
  • snippet (iii): no escape & alloc on the stack as expected

Proposal

I propose to adopt a more aggressive strategy to allocate slices on the stack whenever possible,
whether the capacity is known at build time or not.

Snippet (ii) should be detected as no escape (I believe it is at some point, and we revert to escaping because of the
variable capacity). This would make the escape analysis consistent with maps.

The decision to allocate non-escaping slices to the stack or to the heap should be deferred to runtime,
favoring stack whenever the capacity fits and resorting to heap only for the larger slices.

At the very least, this should favor well-abiding functions that provide a predictable capacity in the call to make (such as snippet (ii)). Growing dynamically the slice is probably a case we could still leave to the heap.

@fredbi fredbi added the Proposal label Feb 1, 2023
@gopherbot gopherbot added this to the Proposal milestone Feb 1, 2023
@fredbi
Copy link
Author

fredbi commented Feb 1, 2023

See also a related proposal regarding maps: #58214

@fredbi fredbi changed the title proposal: affected/package: runtime : allow no-escape slices of unknown capacity to be allocated to the stack more aggressively proposal: runtime : allow no-escape slices of unknown capacity to be allocated to the stack more aggressively Feb 1, 2023
@randall77
Copy link
Contributor

Currently all of our stack frames are fixed size, so allocating anything of dynamic size on the stack is a big project.

You can simulate what you want like this:

	index := append(make([]int, 0, 64), make([]int, n)...)

That will stack-allocate if n <= 64 and heap allocate otherwise.

I guess we could do that transformation automatically, but we'd need to know the right number for 64, which I think depends on the application.

@randall77 randall77 changed the title proposal: runtime : allow no-escape slices of unknown capacity to be allocated to the stack more aggressively runtime : allow no-escape slices of unknown capacity to be allocated to the stack more aggressively Feb 1, 2023
@randall77 randall77 modified the milestones: Proposal, Unplanned Feb 1, 2023
@randall77
Copy link
Contributor

Taking out of the proposal process, as this is just an optimization. There's no new API.

@mknyszek mknyszek changed the title runtime : allow no-escape slices of unknown capacity to be allocated to the stack more aggressively runtime: allow no-escape slices of unknown capacity to be allocated to the stack more aggressively Feb 1, 2023
@mknyszek
Copy link
Contributor

mknyszek commented Feb 1, 2023

To add on to what @randall77 said, we briefly considered some kind of off-stack-but-bound-to-stack-frames allocator for dynamically-sized things (and other things that escape for some of the more "trivial" reasons), but the total win is unclear when there are workarounds that applications are using (like what @randall77 pointed out).

@mknyszek mknyszek added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Feb 1, 2023
@fredbi
Copy link
Author

fredbi commented Feb 1, 2023

Thank you @randall77 for the above hint . Will try it out. This is indeed a bit of a hack...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Projects
None yet
Development

No branches or pull requests

4 participants