| ▲ | knorker 4 days ago |
| Are you sure this is what's happening? Looks to me like the slice object is returned by value, and the array was always on the heap. See https://go.dev/play/p/Bez0BgRny7G (the address of the slice object changed, so it's not the same object on the heap) Sure, Go has escape analysis, but is that really what's happening here? Isn't this a better example of escape analysis: https://go.dev/play/p/qX4aWnnwQV2 (the object retains its address, always on the heap, in both caller and callee) |
|
| ▲ | simiones 4 days ago | parent | next [-] |
| 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. |
|
|
|
| ▲ | masklinn 4 days ago | parent | prev | next [-] |
| That’s the one. Since 1.17 it’s not impossible for escape analysis to come into play for slices but afaik that is only a consideration for slices with a statically known size under 64KiB. |
|
| ▲ | bonniesimon 4 days ago | parent | prev | next [-] |
| Interesting! This could be true. I'll play around with this in a bit. |
| |
| ▲ | knorker 4 days ago | parent [-] | | Yeah I think what you're describing, returning a slice thus copying a reference to the same array (but not copying the array), then destroying the callee slice not causing the array to be freed, is just basic garbage collection logic, not escape analysis. |
|
|
| ▲ | lenkite 4 days ago | parent | prev [-] |
| Yes, this is merely the slice fat pointer being copied and returned. |