

"b = &boy{}" vs "*b = boy{}" 谁不讲武德?golang 逃逸分析...
source link: https://my.oschina.net/p1gd0g/blog/4816627
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近想要将 protobuf 变量和之前设计的数据对象整合起来,维护在内存中,以减少内存申请和 GC 的性能损耗。
feature or bug,gogoproto 解码疑惑
由于 gogoproto 在 unmarshal
时不保证输入和输出一致,作为结果的指针变量和输入的字节切片可能不一致(比如说,在 unmarshal
slice 时没有 reset
操作)。我们需要对这个指针变量进行重置,pb 生成文件的 reset
实现方法如下。
func (m *Data) Reset() { *m = Data{} }
在看到 Data{}
时我陷入了疑惑,按我的理解,这一步是需要申请内存的。那么如此一来,我们在将某个 pb 变量抛入内存时不可避免的还是需要申请内存,这样本次的研发需求好像失去了意义。
我的第一反应是,这是 gogoproto 的问题,也许官方 go proto 不是这样的。可是重新生成后发现 reset
方法实现并没有什么区别。只不过官方 go proto 会在 unmarshal
时主动 reset
。
那么,难道一开始的方向就错了吗?啊头秃。
柳暗花明又一村
不死心的我开始看各种文档,包括 gogoproto 的各种插件,可惜并没有找到有用的内容。接着我又开始看官方 proto 文档。。。
这时我发现了一点蛛丝马迹。
在日常使用 protobuf 时,如果不复用旧的变量,我们一般会
- 声明指针变量,
data := &pb.Data{}
; - 解码,
proto.Unmarshal(bytes, data)
。
显然,第一步是需要申请内存。而按照 go proto 的源码,unmarshal
时的 reset
操作又会申请一次内存,难道 Google 会允许这种性能损耗?
真的吗,我不信。
逃逸分析入门
想的太多,不如写个 benchmark 试一下。(小心 microbenchmark 的一些坑)
benchmark
package main
import (
"testing"
)
type boy struct {
name string
age int
}
var b1 = &boy{}
var b2 = &boy{}
func Benchmark_1(b *testing.B) {
for i := 0; i < b.N; i++ {
temp := &boy{}
b1 = temp
}
}
func Benchmark_2(b *testing.B) {
for i := 0; i < b.N; i++ {
temp := &boy{}
*b1 = *temp
}
}
func Benchmark_3(b *testing.B) {
for i := 0; i < b.N; i++ {
temp := &boy{}
*b1 = *temp
b2 = temp
}
}
结果如下。
goos: linux
goarch: amd64
pkg: bible
Benchmark_1-4 29142411 42.2 ns/op 32 B/op 1 allocs/op
Benchmark_2-4 1000000000 0.711 ns/op 0 B/op 0 allocs/op
Benchmark_3-4 28474614 39.5 ns/op 32 B/op 1 allocs/op
PASS
ok bible 3.258s
结果是一目了然的,temp := &boy{}
确实没有重复申请内存。
到此,只需要逐个分析就好。首先打开编译报告看一下。
go build -gcflags "-m -m"
var b1 = &boy{}
//go:noinline
func main() {
temp := &boy{}
// &boy literal escapes to heap:
// flow: temp = &{storage for &boy literal}:
// from &boy literal (spill) at ./main.go:12:10
// from temp := &boy literal (assign) at ./main.go:12:7
// flow: {heap} = temp:
// from b1 = temp (assign) at ./main.go:13:5
// &boy literal escapes to heap
b1 = temp
}
新创建的 &boy{}
被全局变量引用,于是逃逸到堆上,成为动态变量,无法被重复利用。
var b1 = &boy{}
//go:noinline
func main() {
temp := &boy{}
// &boy literal does not escape
*b1 = *temp
}
*b1 = *temp
仅仅是赋值操作,新创建的 &boy{}
没有被引用,留在栈上,后续被重复利用。
感谢 @Dash 同学的支持~

Recommend
-
17
二手车电商赛道变了天。最近多家媒体曝光, 人人车 作价1万港元(约合人民币8586元)拟将主要资产贱卖,这距离其上次获得融资已过去两年半的时间。时间再往前推,优信二手车今年以来先...
-
18
编者按:本文系专栏作者投稿,作者互联网江湖。 “原来合作的几乎没继续合作的,今年双十一也没有什么商家合作,合作了也拿不到...
-
13
整理 | 褚杏娟、Tina 苹果的新系统不仅漏洞百出,还对自家应用程序设置“双标”进行...
-
21
在这个行业越来越向头部集中的时代,如果被头部企业甩开,今后的道路必将更加难走。
-
8
2020-12-17 16:20口头offer不作数?大厂HR也不讲武德本文来自微信公众号:互联网坊间八卦(ID:kekesil),作者:职场八卦事,原标题:《字节跳动这位HR,今年这个“F”,你...
-
11
大公司,你不讲武德!大数据话题下的优秀回答者 大家好,我是大数据界唯一真正老仁波茄。刚才有个朋友问我:刘老师发生肾么事了?给我发过来一张截图,原来是佐天,我的文章被...
-
13
消失的菜市场:互联网巨头不讲武德吞噬一切?
-
8
年轻人不讲武德,竟然重构出这么优雅后台 API 接口 Published on Dec 2, 2020...
-
16
我被Hr领进了一个小黑屋,让我在...
-
8
韭民炸锅!又有公司不讲武德 寿司英雄 昨天21:33 这是一个并不平静的周末。不讲武德的公司继续出来造作了。前有“买醉骗涨停”与景芝酒业重组告吹的ST亚星,后有定增159亿大股东高卖低买的900亿大牛股天齐锂业,...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK