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
Closed

cmd/compile: reuse allocated value in interface #23677

dsnet opened this issue Feb 3, 2018 · 5 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. Performance
Milestone

Comments

@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).

@cznic
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 the NeedsFix The path to resolution is known, but the work has not been done. label Mar 28, 2018
@ianlancetaylor ianlancetaylor added this to the Go1.11 milestone Mar 28, 2018
@bradfitz bradfitz modified the milestones: Go1.11, Unplanned May 18, 2018
@mvdan
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
Copy link
Contributor

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

@mvdan
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
Copy link
Contributor

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

@golang golang locked and limited conversation to collaborators Dec 26, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. Performance
Projects
None yet
Development

No branches or pull requests

7 participants