Golang:如何解释类型断言的效率?

huangapple go评论81阅读模式
英文:

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 (
	&quot;fmt&quot;
	&quot;time&quot;
)

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 &lt; 500000000; a++ {
		_ = i.(*A)
	}
}

func testE2I() {
	var i interface{} = new(A)
	for a := 0; a &lt; 500000000; a++ {
		_ = i.(I)
	}
}

func main() {
	fmt.Println(&quot;testE2I:&quot;)
	run(testE2I)
	fmt.Println(&quot;testE2T:&quot;)
	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.

huangapple
  • 本文由 发表于 2015年7月23日 11:10:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/31577540.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定