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

cmd/compile: escape analysis for backing arrays #42165

Open
snadrus opened this issue Oct 23, 2020 · 7 comments
Open

cmd/compile: escape analysis for backing arrays #42165

snadrus opened this issue Oct 23, 2020 · 7 comments
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@snadrus
Copy link

snadrus commented Oct 23, 2020

Go's automatic memory management doesn't rely entirely on garbage collection. Escape analysis provides a faster solution where possible.
Lets apply escape analysis to backing arrays (the arrays behind slices).

Example

In the next example, if append grows the array, it could put the old array directly back into the free lists:

func filter(haystack []X, predicate func(X) bool) []X {
  var result []X
  for _, v := range haystack {
    if predicate(v) {
      result = append(result, v)               // result may grow many times. 
  }
  return result
}

An alternate append-grow that returns the heap memory to a thread-local free list could be proven safe.

The compiler can verify that no references to intermediate slice values survive later iterations, so there are zero references at the function end to previous backing arrays. If an intermediate was used in a function call, this would follow the regular escape analysis logic.

Collision with GC

To avoid Garbage Collection competition, these non-escaping slices could be removed from stack maps. Their capturing func would need an implicit defer-type behavior to add them to GC when returned.

 defer func(){ retValGC = retValNoEscape }()      // added by compiler. Shouldn't affect line numbers, etc.

This also enables panic() scenarios to not leak. This is perhaps too literal as all that would happen would be a stack map reference.

Result

Garbage Collection would be reduced and Go programs would use considerably less memory-over-time (derivative). No language changes would be necessary, and the Go1 promise is kept. Potentially the same work will be completed with less total CPU.

Maps' private slices should be able to benefit from this too as they only grow when a single thread is writing and no other activity is taking-place. I doubt escape analysis can determine this, so it may need to be hard-coded.

Future

Future advances in Escape Analysis would help more cases of Go's automatic memory management to have a minimal footprint in all but the most especially-complex cases where Garbage Collection is the most reasonable solution.

@gopherbot gopherbot added this to the Proposal milestone Oct 23, 2020
@ianlancetaylor
Copy link
Contributor

There is no user-visible change suggested here, so no need to use the proposal process. I will change to a suggestion for the compiler.

@ianlancetaylor ianlancetaylor changed the title Proposal: Escape analysis for backing arrays cmd/compile: escape analysis for backing arrays Oct 23, 2020
@ianlancetaylor ianlancetaylor added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. and removed Proposal labels Oct 23, 2020
@ianlancetaylor ianlancetaylor modified the milestones: Proposal, Backlog Oct 23, 2020
@mdempsky
Copy link
Member

mdempsky commented Nov 1, 2020

I think this requires flow-sensitive analysis, which escape analysis isn't currently setup for. This might be more feasible once escape analysis moves to SSA.

It also requires the runtime to prove a "free" operation that the compiler could emit calls to for allocations that are known to no longer be in use.

@adonovan
Copy link
Member

adonovan commented Mar 4, 2022

I suspect there would be quite a lot of opportunities for improvement if escape analysis ran on the SSA form, after inlining. All those small functions that allocate, populate, and return a slice only for the caller to iterate over it could avoid generating garbage if the compiler emitted explicit calls to free; and when inlining causes the array size to become a constant, the array can simply be stack allocated.

@randall77
Copy link
Contributor

Escape analysis already runs after inlining. For instance, the inlined version of f does not allocate, even though the uninlined version of f does allocate.

package main

func f() []byte {
	x := [4]byte{'a','b','c','d'}
	return x[:]
}

func main() {
	a := f()
	println(string(a))
}

@adonovan
Copy link
Member

adonovan commented Mar 4, 2022

Sorry, I didn't mean to imply that escape analysis happened before inlining, only that the two are synergistic... but I guess inlining is synergistic with nearly everything.

@snadrus
Copy link
Author

snadrus commented Mar 4, 2022 via email

@adonovan
Copy link
Member

adonovan commented Mar 4, 2022

See also:

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.
Projects
None yet
Development

No branches or pull requests

6 participants