**已关闭。**此问题为not reproducible or was caused by typos。当前不接受答案。
这个问题是由一个错字或一个无法再复制的问题引起的。虽然类似的问题可能是on-topic在这里,但这个问题的解决方式不太可能帮助未来的读者。
昨天关门了。
Improve this question
如果一个函数声明了命名的返回值,并且其中有多个return语句,常见的情况是这样的:
func foo() (bar Bar, baz Baz, err error) {
// other code
if condition {
err = fmt.Errorf("specific error description")
return
}
// other code
return
}
当condition
为true时,将设置err
命名返回值,但其他返回值(bar
和baz
)保持不变。
有没有办法替代
err = fmt.Errorf("specific error description")
return
用一句俏皮话
当然,这一行可以工作并给予正确的结果:
return bar, baz, fmt.Errorf("specific error description")
但是,编译器是否足够聪明,知道不需要将bar
和baz
的值分别赋值给它们自己?
由于我不熟悉编译器实现的细节,所以我尝试了gobench
来查看差异。
下面是我的测试代码:
const fieldSize = 32
type foo struct {
bigField [fieldSize]byte
}
func genFoo() (result1 foo, result2 foo, ok bool) {
result1.bigField[fieldSize/2] = 1
result2.bigField[fieldSize-1] = 8
return result1, result2, true
}
func genFoo2() (result1 foo, result2 foo, ok bool) {
result1.bigField[fieldSize/2] = 1
result2.bigField[fieldSize-1] = 8
return result2, result1, true
}
func genFoo3() (result1 foo, result2 foo, ok bool) {
result1.bigField[fieldSize/2] = 1
result2.bigField[fieldSize-1] = 8
ok = true
return
}
func Benchmark_genFoo(b *testing.B) {
for i := 0; i < b.N; i++ {
result1, result2, ok := genFoo()
if !(ok && result1.bigField[fieldSize/2] == 1 && result2.bigField[fieldSize-1] == 8) {
b.Fatalf("Incorrect")
}
}
}
func Benchmark_genFoo2(b *testing.B) {
for i := 0; i < b.N; i++ {
result1, result2, ok := genFoo2()
if !(ok && result2.bigField[fieldSize/2] == 1 && result1.bigField[fieldSize-1] == 8) {
b.Fatalf("Incorrect")
}
}
}
func Benchmark_genFoo3(b *testing.B) {
for i := 0; i < b.N; i++ {
result1, result2, ok := genFoo3()
if !(ok && result1.bigField[fieldSize/2] == 1 && result2.bigField[fieldSize-1] == 8) {
b.Fatalf("Incorrect")
}
}
}
genFoo()
是“一行程序”版本。genFoo2()
使用完全相同的语法编写,但return语句交换了result1
和result2
genFoo3()
是“多线程”版本。
结果是:
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
Benchmark_genFoo
Benchmark_genFoo-12 166055121 7.085 ns/op
Benchmark_genFoo2
Benchmark_genFoo2-12 134723227 8.945 ns/op
Benchmark_genFoo3
Benchmark_genFoo3-12 166496562 7.163 ns/op
使用fieldSize = 32
,genFoo()
和genFoo3()
的性能几乎相同。
另一方面,genFoo2()
尽管语法相同,但速度较慢。
使用fieldSize = 1000000
,结果将是:
Benchmark_genFoo
Benchmark_genFoo-12 10000 113464 ns/op
Benchmark_genFoo2
Benchmark_genFoo2-12 5528 215486 ns/op
Benchmark_genFoo3
Benchmark_genFoo3-12 10000 112115 ns/op
genFoo()
和genFoo3()
仍然彼此接近,而genFoo2()
明显较慢。
对于fieldSize = 100000000
,结果将是:
Benchmark_genFoo
Benchmark_genFoo-12 12 91378267 ns/op
Benchmark_genFoo2
Benchmark_genFoo2-12 10 102982670 ns/op
Benchmark_genFoo3
Benchmark_genFoo3-12 20 55025230 ns/op
哎呀,genFoo()
在这个例子中失败了,尽管它的行为与genFoo2()
不同。
但为什么呢?
1条答案
按热度按时间5uzkadbs1#
如果你希望听到这样的俏皮话
它的性能和
genFoo3()
完全一样,不幸的是,它在go中不存在。我可以回答你提到的一些性能问题,以及go中命名结果的底层实现如何解释为什么你看到
genFoo
,genFoo2
,genFoo3
之间的性能差异。对于未命名和已命名的返回函数:
func f(a int) (b int)
和func g(a int) int
,它们的堆栈分配略有不同。因为你的函数都有命名的返回值,
result1
,result2
和ok
已经在堆栈中命名了。所以genFoo2
看起来比genFoo
慢很多,因为需要在result1
和result2
之间进行交换,这是更昂贵的。return语句genFoo2
将执行相当于至于为什么
genFoo
比genFoo3
慢,编译器可能在genFoo
的return语句中试图复制result1
和result2
(而genFoo3
不需要这样的操作)。尽管你可以使用-gcflags -m
检查你的特定版本是否如此。