-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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: passing function argument via interface is 16x slower than via typed value #17118
Comments
This is expected since Go 1.4 or Go 1.5, when interfaces were changed to only hold 0 or 1 pointer directly in their data word. In your example it's having to store a pointer to your int, and then dereference it. There's no bug here, so closing. If you had a specific implementation proposal that I would've kept this open. |
It looks like that all the types that fit a machine word (including CPU profiling shows that the majority of time is spent inside I thought that the compiler could be smarter in this case:
@josharian , could you look into this? |
I can't look into this myself, since I'm leaving on vacation in a few hours. But I'm going to reopen this in case anyone (@martisch?) wants to investigate creating a few specialized convT2E variants. |
We don't store all types that fit into a machine word by value any more. We used to, but we had to change that as part of moving to a precise garbage collector (1.3? 1.4? I forget now). Things that aren't pointers are now stored by reference. So to make an int into an interface we have to allocate an int on the heap and store a *int in the interface data word. It would be nice if we could allocate that int on the stack instead of the heap. I think that is #13807 or #8618. |
But benchmark results say there are no memory allocations when passing
edit: func BenchmarkEfaceArg(b *testing.B) {
n := 0
for i := 0; i < b.N; i++ {
x := i
n += fEface(x)
}
if b.N > 0 && n == 0 {
b.Fatalf("n shouldn't be zero. b.N=%d", b.N)
}
} No |
That's probably escape analysis. What does |
Looks like we are allocating that int on the stack, not the heap, sorry. |
CL https://golang.org/cl/29373 mentions this issue. |
No point in calling a function when we can build the interface using a known type (or itab) and the address of a local. Get rid of third arg (preallocated stack space) to convT2{I,E}. Makes go binary smaller by 0.2% benchmark old ns/op new ns/op delta BenchmarkEfaceInteger-8 16.7 10.1 -39.52% Update #17118 Update #15375 Change-Id: I9724a1f802bfa1e3957bf1856b55558278e198a2 Reviewed-on: https://go-review.googlesource.com/29373 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
@randall77 , results are way better now:
There is no
|
All of those calls are from |
Yeah, the following func fEface(v interface{}) int {
if x, ok := v.(int); ok {
return x+1
}
return 0
}
I think the issue may be closed now. |
@randall77 , how about automatic rewriting of the following code: x := v.(int) into x, ok := v.(int) // fast path
if !ok {
// slow path
panic(fmt.Sprintf("cannot convert %T into int", v))
} during compilation? Then the |
Yes, that's what I meant by "inline the common case where the type matches". |
What version of Go are you using (
go version
)?What operating system and processor architecture are you using (
go env
)?What did you do?
I benchmarked the following code:
Below are benchmark results:
What did you expect to see?
Both benchmarks should run with comparable speed.
What did you see instead?
The benchmark passing the argument via interface to
fEface
is 16x slower than the benchmark passing the argument tofInt
.The text was updated successfully, but these errors were encountered: