Remix.run Logo
simiones 4 days ago

Depending on escape analysis, the array underlying the slice can get allocated on the stack as well, if it doesn't escape the function context. Of course, in this case, because we are returning a pointer to it via the slice, that optimization isn't applicable.

knorker 4 days ago | parent [-]

Agreed that it could in principle. But I can't immediately get it to do so: https://go.dev/play/p/9hLHattS8cf

Both arrays in this example seem to be on the heap.

simiones 3 days ago | parent [-]

Taking the address of those variables makes them escape to heap. Even sending them to the Printf function makes them escape to heap.

If you want to confirm, you have to use the Go compiler directly. Take the following code:

  package main
  import (
    "fmt"
  )
  type LogEntry struct {
    s string
  }
  func readLogsFromPartition(partition int) []LogEntry {
    var logs []LogEntry // Creating an innocent slice
    logs = []LogEntry{{}}
    logs2 := []LogEntry{{}}
    fmt.Printf("%v %v\n", len(logs), len(logs2))
    return []LogEntry{{}}
  }
  func main() {
    logs := readLogsFromPartition(1)
    fmt.Printf("%p\n", &logs[0])
  }
And compile it with

  $ go build -gcflags '-m' main.go
  # command-line-arguments
  ./main.go:15:12: inlining call to fmt.Printf
  ./main.go:21:12: inlining call to fmt.Printf
  ./main.go:13:19: []LogEntry{...} does not escape
  ./main.go:14:21: []LogEntry{...} does not escape
  ./main.go:15:12: ... argument does not escape
  ./main.go:15:27: len(logs) escapes to heap
  ./main.go:15:38: len(logs2) escapes to heap
  ./main.go:16:19: []LogEntry{...} escapes to heap
  ./main.go:21:12: ... argument does not escape
However, if you return logs2, or if you take the address, or if you pass them to Printf with %v to print them, you'll see that they now escape.

An additional note: in your original code from your initial reply, everything you allocate escapes to heap as well. You can confirm in a similar way.