英文:
golang: how to explain the type assertion efficiency?
问题
类型断言将涉及对runtime.assertE2T或runtime.assertE2I的调用(您可以查看汇编代码)。
package main
import (
"fmt"
"time"
)
type I interface {
echo()
}
type A struct{}
func (a *A) echo() {}
type testfn func()
func run(f testfn) {
ts := time.Now()
f()
te := time.Now()
fmt.Println(te.Sub(ts))
}
func testE2T() {
var i interface{} = new(A)
for a := 0; a < 500000000; a++ {
_ = i.(*A)
}
}
func testE2I() {
var i interface{} = new(A)
for a := 0; a < 500000000; a++ {
_ = i.(I)
}
}
func main() {
fmt.Println("testE2I:")
run(testE2I)
fmt.Println("testE2T:")
run(testE2T)
}
结果:
testE2I:
11.065225934s
testE2T:
5.720773381s
似乎类型断言比C中的指针转换要慢?如何解释这个现象?
另外,当我使用gccgo运行相同的程序时,会导致内存溢出错误。gccgo在垃圾回收方面是否有一些限制?
英文:
The type assertion would involve call to runtime.assertE2T or runtime.assertE2I (you could see the assembly codes).
package main
import (
"fmt"
"time"
)
type I interface {
echo()
}
type A struct{}
func (a *A) echo() {}
type testfn func()
func run(f testfn) {
ts := time.Now()
f()
te := time.Now()
fmt.Println(te.Sub(ts))
}
func testE2T() {
var i interface{} = new(A)
for a := 0; a < 500000000; a++ {
_ = i.(*A)
}
}
func testE2I() {
var i interface{} = new(A)
for a := 0; a < 500000000; a++ {
_ = i.(I)
}
}
func main() {
fmt.Println("testE2I:")
run(testE2I)
fmt.Println("testE2T:")
run(testE2T)
}
result:
testE2I:
11.065225934s
testE2T:
5.720773381s
It seems that the type assertion is slower than the pointer cast in C? How to explain it?
And it's strange that when I use gccgo to run the same program, it would cause out-of-memory error. Does the gccgo has some limitation in gc?
答案1
得分: 4
我无法完全弄清楚你的主要问题是什么,但我会尽力回答你提出的问题。
在C语言中,类型断言似乎比指针转换慢?
是的,确实如此。类型断言需要在运行时进行安全检查,因此需要执行一系列的检查。对于接口到接口的断言来说情况更糟糕,因为你还需要确保类型实现了接口。
话虽如此,它们确实可以更高效地执行。实际上,这里是你在Go 1.4.2和最新的Go 1.5开发版本上进行基准测试的结果对比:
-
Go 1.4.2:
testE2I: 10.014922955s, testE2T: 4.465621814s
-
Go 1.5 dev:
testE2I: 7.201485053s, testE2T: 287.08346ms
现在快了十倍以上,而Go 1.6的新SSA后端可能会带来更好的优化。
另外,当我使用gccgo运行相同的程序时,会导致内存溢出错误。gccgo在垃圾回收方面有一些限制吗?
我猜测这可能是gccgo缺乏逃逸分析的原因,但我可能错了。实际上,我在我的机器上使用gccgo运行了基准测试,但它消耗了约9GB的内存,这绝对不正常。我相信提交一个问题可能会有所帮助。无论如何,这是使用-O3
选项的结果:
- gccgo:
testE2I: 30.405681s, testE2T: 1.734307s
对于具体类型来说更快,但对于接口到接口的断言来说要慢得多。
英文:
I can't quite figure out what's your main question, but I'll try to answer the ones you asked as best as I can.
>It seems that the type assertion is slower than the pointer cast in C?
Yes, it is. Type assertions need to be safe at runtime, thus there is a number of checks they need to perform. It's even worse with interface-to-interface assertion, because you also need to ensure that the type implements the interface.
With that said, they can definitely perform better. In fact, here is comparison of your benchmark results on Go 1.4.2. vs latest dev version of Go 1.5:
-
Go 1.4.2:
testE2I: 10.014922955s, testE2T: 4.465621814s
-
Go 1.5 dev:
testE2I: 7.201485053s, testE2T: 287.08346ms
It's more than ten times faster now, and Go 1.6's new SSA backend might bring even better optimisations.
>And it's strange that when I use gccgo to run the same program, it would cause out-of-memory error. Does the gccgo has some limitation in gc?
My guess it that it's gccgo's lack of escape analysis, but I may be wrong. I was actually able to run the benchmark with gccgo on my machine, but it consumed about 9 GB of RAM, which is everything but normal. I'm pretty sure that filing an issue about that wouldn't hurt. Anyway, here are the results with -O3
:
- gccgo:
testE2I: 30.405681s, testE2T: 1.734307s
Faster on the concrete type, but much slower with interface-to-interface assertion.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论