Skip to content

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

@fredbi

Description

@fredbi

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Performance

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions