Go1.23 新特性:新标准库 unique,性能更好,开销更小!

大家好,我是煎鱼。

Go1.23 吵来吵去的,现在已经基本尘埃落定了。在我开始写这个新版本特性系列时,Go1.23 rc2 已经发布了有一周多:

2a072616b123345b005383ee4742a017.png

今天我们分享的是新的标准库 unique 的介绍和快速入门。

背景

基于 Go unique 官方提案,我简化了一下内容。要做这个主要原因是:Go 缺乏运行时的驻留支持,这与其他语言存在差距。

多年来,Go 社区对弱映射(weak map)和字符串驻留(string interning)的需求已在过去几年的 GitHub 问题中有所体现。

来自 go/issues/62483 给出的动机:

2dc1c1c0e73d289e8965e72f599aa109.png

虽然社区中要求这些功能的人很少,但我们看到了 go4.org/intern 包没有内置支持这一功能的后果(简单看了下代码。应该指的是:unsafe 骚操作,会动到 Go 的垃圾回收器逻辑,理论上要跟着 Go 版本调整。挺折腾!)

字符串驻留是什么?

前面的背景存在一些专业名词。尤其是字符串驻留是什么?可能会看的有些懵。这里我们补充一下基础知识。

根据 GPT-4 的概要总结如下:

1、字符串驻留(string interning)是一种在计算机科学中用于优化内存使用和提高性能的技术。

2、主要思想是对于每一个唯一的字符串值,只存储一个副本,这些字符串必须是不可变的。

3、以下是有关字符串驻留的一些关键点:

  • 定义

    • 字符串驻留是一种存储技术,确保每个独特的字符串值在内存中只存在一个副本。

    • 这意味着如果两个字符串具有相同的值,它们将共享同一个内存地址,而不是每个字符串都占用独立的内存空间。

  • 优势

    • 节省内存:通过避免重复的字符串副本,可以显著减少内存消耗。

    • 提高性能:字符串比较操作可以通过比较内存地址而不是逐字符比较来实现,从而加快速度。

标准库 unique

标准库 unique[1],文档非常的短小精悍。这次 Go 官方连个 example 都没有直接给。

在该标准库,unique 会对所有被添加的值进行全局的并发安全缓存,以确保值的唯一性和有效重用。会做到运行时的驻留支持,以此达到开销较佳。

API 如下:

fbb99d13972eef0d2e11ca7a8843f987.png
  • Handle 是 T 类型值的全局唯一标识。

  • Make 方法为 T 类型的值返回一个全局唯一的 Handle

  • Handle[T].Value 方法返回产生 Handle 的 T 值的浅拷贝副本。

一眼看到底。比较直接,这个标准库就是围绕着 unique.Handle 来用。

到底怎么用和有什么好处,通过一个例子就能快速了解优点了。

这个新特性代码片段来自 @Anton 大佬的分享。(还是社区的力量大,不像官方文档一个例子都不给)

在以前我们要用 Go 写一个随机词生成器,可以这么写。

生成单词的代码如下:

func wordGen(nDistinct, wordLen int) func() string {
 vocab := make([]string, nDistinct)
 for i := range nDistinct {
  word := randomString(wordLen)
  vocab[i] = word
 }
 return func() string {
  word := vocab[rand.Intn(nDistinct)]
  return strings.Clone(word)
 }
}

func randomString(n int) string {
 // 脑子进煎鱼了
 const letters = "eddycjyabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 ret := make([]byte, n)
 for i := 0; i < n; {
  b := make([]byte, 1)
  if _, err := rand.Read(b); err != nil {
   panic(err)
  }
  ret[i] = letters[int(b[0])%len(letters)]
  i++
 }
 return string(ret)
}

基于上述方法,我们生成 10000 个单词。看看要使用多少内存。

代码如下:

var words []string

func main() {
 const nWords = 10000
 const nDistinct = 100
 const wordLen = 40
 generate := wordGen(nDistinct, wordLen)
 memBefore := getAlloc()

 words = make([]string, nWords)
 for i := range nWords {
  words[i] = generate()
 }

 memAfter := getAlloc()
 memUsed := memAfter - memBefore
 fmt.Printf("Memory used: %dKB\n", memUsed/1024)
}

func getAlloc() uint64 {
 var m runtime.MemStats
 runtime.GC()
 runtime.ReadMemStats(&m)
 return m.Alloc
}

本地运行的输出结果是:

// 煎鱼
$ go run main.go
Memory used: 622KB

现在改用 Go1.23 的新标准库 unique 来写。代码如下:

var words []unique.Handle[string]

func main() {
 const nWords = 10000
 const nDistinct = 100
 const wordLen = 40
 generate := wordGen(nDistinct, wordLen)
 memBefore := getAlloc()

 words = make([]unique.Handle[string], nWords)
 for i := range nWords {
  words[i] = unique.Make(generate())
 }

 memAfter := getAlloc()
 memUsed := memAfter - memBefore
 fmt.Printf("Memory used: %dKB\n", memUsed/1024)
}

输出结果:

// 煎鱼在 Go Playground 执行的结果
Memory used: 95KB

内存使用从 622KB 减少到 95KB!优化效果非常明显。而且生成的数量越多,理论上优化效果更大。

为什么那么快就接纳了

如果有经常关注 Go 社区响应的同学,看到背景后可能会想到。这么小众的场景(提出者自己说的),居然这么一帆风顺的就直接过了。还很快来到了正式版本?

其实能对垃圾回收(GC)做这么骚操作,还不被喷的。社区上会这么做的人不多。

因此无论是之前的 go4org/intern 库,还是这次新标准库 unique 提案。相关作者都是 Google 和 Go 团队里的关联者。当然推进的极快了!

16f0409c198cf777c1ada5ffb87f82bc.png
unique 提案提出者
aa854758571f00ab10a957ea2bc47d2e.png
go4org/intern 贡献者

总结

本次新标准库 unique 是基于 go4org/intern 库内化而来,虽然都是 Google 自己人开发的。但是该库的加入对于 Go 在运行时的驻留支持增添了一笔新力量。

一句题外话,官方对于自己的人的库真的是一路绿灯。文档和说明都非常的简洁。莫非是压根不想让别人用吧。

推荐阅读

参考资料

[1]

unique: https://pkg.go.dev/unique@master

关注和加煎鱼微信,

一手消息和知识,拉你进技术交流群👇

6b774094321819c2a956fc292e303c82.jpeg

8f656e05ea531a608016a0b9a6a486c1.png

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

原创不易 点赞支持

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip毕设项目-基于Java开发的智慧养老院信息管理系统源码+数据(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值