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: reuse allocated value in interface #23677

Closed
dsnet opened this Issue Feb 3, 2018 · 5 comments

Comments

Projects
None yet
6 participants
@dsnet
Copy link
Member

dsnet commented Feb 3, 2018

Consider the following snippet:

var sink interface{}

func Benchmark(b *testing.B) {
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		var s = "hello"
		sink = s
	}
}

This prints:

Benchmark-8   	30000000	        40.4 ns/op	      16 B/op	       1 allocs/op

On every iteration, it is calling runtime.convT2Estring. It should be able to figure out that the runtime.eface._type is identical and also a non-pointer kind. In that situation, it should be able to just reuse that previously allocated value.

This should be safe, since Go does not allow you to take the address of a interface value:

&(sink.(string)) // cannot take the address of sink.(string)

Thus, nothing else should be referencing the underlying value (unless unsafe was used).

@dsnet dsnet added the Performance label Feb 3, 2018

@cznic

This comment has been minimized.

Copy link
Contributor

cznic commented Feb 3, 2018

Thus, nothing else should be referencing the underlying value (unless unsafe was used).

I think var sink2 interface{} = sink does reference the underlying value of sink.

@ianlancetaylor ianlancetaylor added this to the Go1.11 milestone Mar 28, 2018

@bradfitz bradfitz modified the milestones: Go1.11, Unplanned May 18, 2018

@mvdan

This comment has been minimized.

Copy link
Member

mvdan commented Dec 20, 2018

I've run into this too, where I have code like:

type T struct {
    F interface{}
}

func ForEach(strs []string, fn func(T)) {
    for _, s := range strs {
        fn(T{F: s})
    }
}

Where T is part of a public API, and F can hold multiple types, like string and map[string]string. I could replace the interface field with many fields of non-interface types, but that would make the public API uglier.

I could also store the values under pointers or struct pointers, assuming that a pointer can fit into an interface without allocating. But that would also make the API a bit more complex and verbose, when it need not be.

I'm wondering if my API design is just bad and will always incur an allocation per iteration, or if the compiler could do something there.

@josharian

This comment has been minimized.

Copy link
Contributor

josharian commented Dec 21, 2018

Given @cznic's observation, I don't see what we can do here. Am I missing something?

@mvdan

This comment has been minimized.

Copy link
Member

mvdan commented Dec 21, 2018

@josharian kindly pointed out that my case is different, since the value changes at every iteration. So the allocation must be there, at least in the general case.

@josharian

This comment has been minimized.

Copy link
Contributor

josharian commented Dec 26, 2018

Closing as unfixable. Please comment or re-open if you think I have this wrong.

@josharian josharian closed this Dec 26, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.