Skip to content

cmd/compile: possibly unnecessary allocation for interface data #20380

@gobwas

Description

@gobwas

Go version:

$ go version
go version go1.8.1 darwin/amd64

Issue

Say we have this code:

package main

type Fooer interface {
        Foo()
}

// HandleFooer is intended to show that passing interface will not rasie an allocation.
func HandleFooer(f Fooer) {
        // Passing interface Fooer<*Concrete> will not raise an allocation 
        // even if we do some work on f, such as f.(*Concrete).Foo().
}

type Concrete byte

func (c Concrete) Foo() {}

And if we run these benchmarks (with -gcflags="-l"):

package main

import "testing"

func BenchmarkCallFooer(b *testing.B) {
        for i := 0; i < b.N; i++ {
                var c Concrete = 42// Moved to heap.
                Fooer(&c).Foo()
        }
}

func BenchmarkPassFooer(b *testing.B) {
        for i := 0; i < b.N; i++ {
                var c Concrete = 42 // Not moved to heap.
                HandleFooer(Fooer(&c))
        }
}

The output is:

BenchmarkCallFooer-4    100000000               17.6 ns/op             1 B/op          1 allocs/op
BenchmarkPassFooer-4    500000000                2.99 ns/op            0 B/op          0 allocs/op

With -gcflags="-m -m":

./iface_test.go:17: HandleFooer f does not escape
./iface_test.go:22: Fooer(&c) escapes to heap
./iface_test.go:22:     from Fooer(&c).Foo() (receiver in indirect call) at ./iface_test.go:22
./iface_test.go:22: &c escapes to heap
./iface_test.go:22:     from Fooer(&c) (interface-converted) at ./iface_test.go:22
./iface_test.go:22:     from Fooer(&c).Foo() (receiver in indirect call) at ./iface_test.go:22
./iface_test.go:21: moved to heap: c

Expected behavior: &c stays on stack in both cases.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions