go 返回值是否需要尽量返回值,而不返回指针呢?

2023-12-27 23:25:54 +08:00
 dyllen

理由如下:

根据内存逃逸的规则,把函数局部变量的指针返回会逃逸,结果导致 gc 压力变大。

所以是否需要尽量返回值,而不返回指针呢?

比如查询一个数据模型返回它,还有其他许多情况。

3489 次点击
所在节点    Go 编程语言
30 条回复
nagisaushio
2023-12-27 23:35:11 +08:00
小类型尽量返回值,大类型看情况
lianyue
2023-12-27 23:48:31 +08:00
我是 结构体 指针否 值
rekulas
2023-12-27 23:48:34 +08:00
对看情况 有些情况下返回值可能压力更大
Maboroshii
2023-12-27 23:52:01 +08:00
我写 go 基本都有指针,除非是明确返回不可修改的值才用值返回。写 web 应用 gc 压力不存在的,pprof 看下,全耗在序列化上了
BeautifulSoap
2023-12-28 00:44:14 +08:00
不要过早地进行优化
绝大部分项目的性能要求还没高到需要抠这种细节的地步
e7
2023-12-28 09:27:04 +08:00
八股就是这么衍生出来的吗。。。大概按 1L 说的就能避免 80%的你认为的不合理,然后真的哪天出现了性能问题再去 pprof ,而且大概率不会是这个原因
body007
2023-12-28 09:27:57 +08:00
结构体统统用指针,普通类型用值。因为结构体你不知道啥时候加需求往里面加大类型,而且结构体一般会到处传,如果所有地方都用值传递,每次赋值都内存拷贝一份也是有开销的,指针的话只拷贝指针地址更快。

另外 https://www.cnblogs.com/janbar/p/17072751.html 这篇文章探讨了直接赋值的深拷贝问题,即使你值传递,结构体内部有指针,那么这些指针在赋值时也是赋值指针地址。如果用值值类型,你到时候还得思考结构体内部哪些字段是深拷贝,哪些是浅拷贝。

http 提供下面方式克隆对象,就是因为值传递内部字段也存在浅拷贝问题,需要编写深拷贝代码。与其值传递增加心智负担,还不如无脑指针传递。
func (r *Request) Clone(ctx context.Context) *Request {

综上所述,我觉得结构体一律用指针。

我在用的 [go-zero]( https://github.com/zeromicro/go-zero/pull/1211/files#diff-a650192c5b74f391823e44c0b326c07abe5c2544ab386b1ce73ce6b293d78a4c) 框架,在这次改动中将参数值传递改为指针传递,导致我某次升级改了好多文件代码,连大佬都觉得结构体指针传递好些吧。


如果你明确的知道你需要值传递,并且清楚这个对象赋值后内部字段存在浅拷贝也不会影响逻辑,那么可以用值传递。
veightz
2023-12-28 09:41:11 +08:00
https://go.dev/wiki/CodeReviewComments#pass-values

有时候也不要只看内存尺寸,而忽视了栈性能远快于堆性能,以及用指针带来了内存逃逸开销,是需要综合考虑的。
veightz
2023-12-28 09:44:03 +08:00
在写代码的时候,尽量把方法往 值传递优化 和 不可变数据 去设计和实现,对象之海迟早会反噬的…
vultr
2023-12-28 09:45:26 +08:00
我也认为应该尽量用指针,如果为了减少 GC ,可以用 sync.Pool 重用它。
cheng6563
2023-12-28 09:46:07 +08:00
@body007 chan 呢
8355
2023-12-28 09:53:41 +08:00
我的理解是以业务逻辑为核心,99%的业务返回值,而 1%的业务是很明显需要指针的,对于不能完全理解以上描述的开发者先返回值是没问题的。
body007
2023-12-28 10:00:32 +08:00
@cheng6563 你得了解 go 的引用类型,chan 是引用类型,直接返回就是引用。作为返回值,引用类型本身类似指针,返回的就是引用。作为参数在引用类型加指针貌似只有需要修改引用类型的时候才用到吧。
MoYi123
2023-12-28 10:24:18 +08:00
是的, 对于降低 p99 有明显的效果
Ryans
2023-12-28 10:27:13 +08:00
小型数据结构:如果是小型结构或不频繁修改的数据,优先返回值以减少 GC 压力。
大型或复杂对象:对于大型或需要频繁修改的对象,考虑返回指针以避免大量数据复制。
keakon
2023-12-28 10:29:35 +08:00
之前看过这篇,性能上大部分情况下是返回 struct 更快:
https://cloud.tencent.com/developer/article/1861199

不过如果要和 nil 区分,或者最终要放到堆里,那就继续用指针。

然后如果是参数的话,记得是超过 32 字节传指针更快,而且每个字段是单独用一个 MOV 指令来复制的,不是一整块复制的。
chenchengbin
2023-12-28 10:45:38 +08:00
虽然返回指针会导致逃逸,但是存在即合理
dyllen
2023-12-28 10:45:40 +08:00
@Maboroshii
@BeautifulSoap
纯讨论一个点,不加入其他无压力,性能没影响什么的类似的额外影响话题。
chenchengbin
2023-12-28 10:47:56 +08:00
@veightz 字符串直接传不会因为传值复制导致内存激增吗,我的理解会复制一个相同大小的字符串
dyllen
2023-12-28 10:48:49 +08:00
比如有些 orm 库的数据库查询,都不是直接返回结果 model 的指针,而是传指针进去,数据填充到里面。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1003969

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX