标签 GopherCon 下的文章

Go map使用Swiss Table重新实现,性能最高提升近50%

本文永久链接 – https://tonybai.com/2024/11/14/go-map-use-swiss-table

2024月11月5日的Go compiler and runtime meeting notes中,我们注意到了一段重要内容,如下图红框所示:

这表明,来自字节的一位工程师在两年多前提出的“使用Swiss table重新实现Go map”的建议即将落地,目前该issue已经被纳入Go 1.24里程碑

Swiss Table是由Google工程师于2017年开发的一种高效哈希表实现,旨在优化内存使用和提升性能,解决Google内部代码库中广泛使用的std::unordered_map所面临的性能问题。Google工程师Matt Kulukundis在2017年CppCon大会上详细介绍了他们在Swiss Table上的工作

目前,Swiss Table已被应用于多种编程语言,包括C++ Abseil库的flat_hash_map(可替换std::unordered_map)Rust标准库Hashmap的默认实现等。

Swiss Table的出色表现是字节工程师提出这一问题的直接原因。字节跳动作为国内使用Go语言较为广泛的大厂。据issue描述,Go map的CPU消耗约占服务总体开销的4%。其中,map的插入(mapassign)和访问(mapaccess)操作的CPU消耗几乎是1:1。大家可千万不能小看4%这个数字,以字节、Google这样大厂的体量,减少1%也意味着真金白银的大幅节省。

Swiss Table被视为解决这一问题的潜在方案。字节工程师初版实现的基准测试结果显示,与原实现相比,Swiss Table在查询、插入和删除操作上均提升了20%至50%的性能,尤其是在处理大hashmap时表现尤为突出;迭代性能提升了10%;内存使用减少了0%至25%,并且不再消耗额外内存。

这些显著的性能提升引起了Go编译器和运行时团队的关注,特别是当时负责该子团队的Austin Clements。在经过两年多的实验和评估后,Go团队成员Michael Pratt基于Swiss Table实现的internal/runtime/maps最终成为Go map的底层默认实现。

在本文中,我们将简单介绍Swiss Table这一高效的哈希表实现算法,并提前看一下Go map的Swiss Table实现。

在进入swiss table工作原理介绍之前,我们先来回顾一下当前Go map的实现(Go 1.23.x)。

1. Go map的当前实现

map,也称为映射,或字典,或哈希表,是和数组等一样的最常见的数据结构。实现map有两个关键的考量,一个是哈希函数(hash function),另一个就是碰撞处理(collision handling)。hash函数是数学家的事情,这里不表。对于碰撞处理,在大学数据结构课程中,老师通常会介绍两种常见的处理方案:

  • 开放寻址法(Open Addressing)

在发生哈希碰撞时,尝试在哈希表中寻找下一个可用的位置,如下图所示k3与k1的哈希值发生碰撞后,算法会尝试从k1的位置开始向后找到一个空闲的位置:

  • 链式哈希法(拉链法, Chaining)

每个哈希桶(bucket)存储一个链表(或其他数据结构),所有哈希值相同的元素(比如k1和k3)都被存储在该链表中。

Go当前的map实现采用的就是链式哈希,当然是经过优化过的了。要了解Go map的实现,关键把握住下面几点:

  • 编译器重写

我们在用户层代码中使用的map操作都会被Go编译器重写为对应的runtime的map操作,就如下面Go团队成员Keith Randall在GopherCon大会上讲解map实现原理的一个截图所示:

  • 链式哈希

前面提过,Go map当前采用的是链式哈希的实现,一个map在内存中的结构大致如下:


来自Keith Randall的ppt截图

我们看到,一个map Header代表了一个map类型的实例,map header中存储了有关map的元数据(图中字段与当前实现可能有少许差异,毕竟那是几年前的一个片子了),如:

- len: 当前map中键值对的数量。
- bucket array: 存储数据的bucket数组,可以对比前面的链式哈希的原理图进行理解,不过不同的是,Go map中每个bucket本身就可以存储多个键值对,而不是指向一个键值对的链表。
- hash seed: 用于哈希计算的种子,用于分散数据并提高安全性。

通常一个bucket可以存储8个键值对,这些键值对是根据键的哈希值分配到对应的bucket中。

注:在《Go语言第一课》专栏中,有关于Go map工作原理的系统说明,感兴趣的童鞋可以看看。

  • 溢出桶(overflow bucket)

每个bucket后面还会有Overflow Bucket。当一个bucket中的数据超出容量时,会创建overflow bucket来存储多余的数据。这样可以避免直接扩展bucket数组,节省内存空间。但如果出现过多的overflow bucket,性能就会下降。

  • “蚂蚁搬家”式的扩容

当map中出现过多overflow bucket而导致性能下降时,我们就要考虑map bucket扩容的事儿了,以始终保证map的操作性能在一个合理的范围。是否扩容由一个名为load factor的参数所控制。load factor是元素数量与bucket数量的比值,比值越高,map的读写性能越差。目前Go map采用了一个经验值来确定是否要扩容,即load factor = 6.5。当load factor超过这个值时,就会触发扩容。所谓扩容就是增大bucket数量(当前实现为增大一倍数量),减少碰撞,让每个bucket中存放的element数量降下来。

扩容需要对存量element做rehash,在元素数量较多的情况下,“一次性”的完成桶的扩容会造成map操作延迟“突增”,无法满足一些业务场景的要求,因此Go map采用“增量”扩容的方式,即在访问和插入数据时,“蚂蚁搬家”式的做点搬移元素的操作,直到所有元素完成搬移。

Go map的当前实现应该可以适合大多数的场合,但依然有一些性能和延迟敏感的业务场景觉得Go map不够快,另外一个常被诟病的就是当前实现的桶扩容后就不再缩容(shrink)了,这会给内存带来压力。


来自issue 20135的截图

下面我们再来看看swiss table的结构和工作原理。

2. Swiss table的工作原理

就像前面提到的,Swiss table并非来自某个大学或研究机构的论文,而是来自Google工程师在工程领域的”最佳实践”,因此关于Swiss table的主要资料都来自Google的开源C++ library Abseil以及开发者的演讲视频。在Abseil库中,它是flat_hash_map、flat_hash_set、node_hash_map以及node_hash_set等数据结构的底层实现,并且Swiss table的实现在2018年9月正式开源

和Go map当前实现不同,Swiss table使用的不是拉链法,而是开放寻址,但并非传统的方案。下面是根据公开资源画出的一个Swiss table的逻辑结构图(注意:并非真实内存布局):

如果用一个式子来表示Swiss table,我们可以用:

A swiss table = N * (metdata array + slots array)

我们看到:swiss table将所谓的桶(这里叫slot)分为多个group,每个group中有16个slot,这也是swiss table的创新,即将开放寻址方法中的probing(探测key碰撞后下一个可用的位置(slot))放到一个16个slot的group中进行,这样的好处是可以通过一个SIMD指令并行探测16个slot,这种方法也被称为Group Probing

在上图中,我们看到一个Group由metadata和16个slot组成。metadata中存储的是元数据,而slot中存储的是元素(key和value)。Group probling主要是基于metadata实现的,Google工程师的演讲有对group probing实现的细节描述。

当我们向swiss table插入一个元素或是查找一个元素时,swiss table会通过hash函数对key进行求值,结果是一个8字节(64bit)的数。和Go map的当前实现一样,这个哈希值的不同bit功用不同,下图是一个来自abseil官网的示例:

哈希值的高57bit被称为H1,低7bit被称为H2。前者用于标识该元素在Group内的索引,查找和插入时都需要它。后者将被用于该元素的元数据,放在metadata中存储,用于快速的group probing之用,也被称为哈希指纹

每个Group的metadata也是一个16字节数组,每个字节对应一个slot,是该slot的控制字节。这个字节的8个bit位的组成如下:


图来自abseil库官网

metadata中的控制字节有三个状态:

  • 最高位为1,其余全零为空闲状态(Empty),即对应的slot尚未曾被任何element占据过;
  • 最高位为0,后7位为哈希指纹(H2),为对应的slot当前已经有element占据的已使用状态
  • 最高位为1,其他位为1111110的,为对应的slot为已删除状态,后续可以被继续使用。

下面是Abseil开发者演进slide中的一个针对swiss table的迭代逻辑:

通过这幅图可以看出H1的作用。不过这里通过pos = pos + 1进行probing(探测)显然是不高效的!metadata之所以设计为如此,并保存了插入元素的哈希指纹就是为了实现高效的probing,下图演示了基于key的hash值的H2指纹通过SIMD指令从16个位置中快速得到匹配的pos的过程:

虽然有两个匹配项,但这个过程就像“布隆过滤器”一样,快速排除了不可能的匹配项,减少了不必要的内存访问。

由此也可以看到:swiss table的16个条目的分组大小不是随意选择的,而是基于SSE2寄存器长度(128bit, 16bytes)和现代CPU的缓存行大小(64字节)优化的,保证了一个Group的控制字节能被单次SIMD指令处理。

此外swiss table也是通过load factor来判定是否需要对哈希表进行扩容,一旦扩容,swiss table通常是会将group数量增加一倍,然后重新计算当前所有元素在新groups中的新位置(rehash),这个过程是有一定开销的。如果不做优化,当表中元素数量较多时,这个过程会导致操作延迟增加。

最后,虽然多数情况是在group内做probing,但当元素插入时,如果当前Group已满,就必须探测到下一个Group,并将元素插入到下一个Group。这样,在该元素的查找操作中,probing也会跨group进行。

到这里,我们已经粗略了解了swiss table的工作原理,那么Go tip对swiss table当前的实现又是怎样的呢?我们下面就来看看。

3. Go tip版本当前的实现

Go tip版本基于swiss table的实现在https://github.com/golang/go/blob/master/src/internal/runtime/maps下。

由于Go map是原生类型,且有了第一版实现,考虑到Go1兼容性,新版基于swiss table的实现也要继承已有的语义约束。同时,也要尽量避免swiss table自身的短板,Go团队在swiss table之上做了局部改进。比如为了将扩容带来的开销降到最低,Go引入了多table的设计,以支持渐进式扩容。也就是说一个map实际上是多个swiss table,而不是像上面说的一个map就是一个swiss table。每个table拥有自己的load factor,可以独立扩容(table的扩容是一次性扩容),这样就可以将扩容的开销从全部数据变为局部少量数据,减少扩容带来的影响

Go swiss-table based map的逻辑结构大致如下:

我们可以看出与C++ swisstable的最直观不同之处除了有多个table外,每个group包含8个slot和一个control word,而不是16个slot。此外,Go使用了二次探测(quadratic probing), 探测序列必须以空slot结束。

为了实现渐进式扩容,数据分散在多个table中;单个table容量有上限(maxTableCapacity),超过上限时分裂成两个table;使用可扩展哈希(extendible hashing)根据hash高位选择table,且每个table可以独立增长。

Go使用Directory管理多个table,Directory是Table的数组,大小为2^globalDepth。如果globalDepth=2,那Directory最多有4个表,分为0×00、0×01、0×10、0×11。Go通过key的hash值的前globalDepth个bit来选择table。这是一种“extendible hashing”,这是一种动态哈希技术,其核心特点是通过动态调整使用的哈希位数(比如上面提到的globalDepth)来实现渐进式扩容。比如:初始可能只用1位哈希值来区分,需要时可以扩展到用2位,再需要时可以扩展到用3位,以此类推。

举个例子,假设我们用二进制表示哈希值的高位,来看一个渐进式扩容的过程:

  • 初始状态 (Global Depth = 1):
directory
hash前缀  指向的table
0*** --> table1 (Local Depth = 1)
1*** --> table2 (Local Depth = 1)
  • 当table1满了需要分裂时,增加一位哈希值 (Global Depth = 2):
directory
hash前缀  指向的table
00** --> table3 (Local Depth = 2)  // 由table1扩容而成
01** --> table4 (Local Depth = 2)  // 由table1扩容而成
10** --> table2 (Local Depth = 1)
11** --> table2 (Local Depth = 1)  // 复用table2因为它的Local Depth还是1
  • 如果table2也满了,需要分裂:
directory
hash前缀  指向的table
00** --> table3 (Local Depth = 2)
01** --> table4 (Local Depth = 2)
10** --> table5 (Local Depth = 2) // 由table2扩容而成
11** --> table6 (Local Depth = 2) // 由table2扩容而成

通过extendible hashing实现的渐进式扩容,每次只处理一部分数据,扩容过程对其他操作影响小,空间利用更灵活。

对于新版go map实现而言,单个Table达到负载因子阈值时触发Table扩容。当需要分裂的Table的localDepth等于map的globalDepth时触发Directory扩容,这就好理解了。

除此之外,Go版本对small map也有特定优化,比如少量元素(<=8)时直接使用单个group,避免或尽量降低swiss table天生在少量元素情况下的性能回退问题。

更多实现细节,大家可以自行阅读https://github.com/golang/go/blob/master/src/internal/runtime/maps/下的Go源码进行理解。

注:目前swiss table版的go map依然还未最终定型,并且后续还会有各种优化加入,这里只是对当前的实现(2024.11.10)做概略介绍,不代表以后的map实现与上述思路完全一致。

4. Benchmark

目前gotip版本中GOEXPERIMENT=swissmap默认已经打开,我们直接用gotip版本即可体验基于swiss table实现的map。

字节工程师zhangyunhao的gomapbench repo提供了对map的性能基准测试代码,不过这个基准测试太多,我大幅简化了一下,只使用Int64,并只测试了元素个数分别为12、256和8192时的情况。

注:我基于Centos 7.9,使用Go 1.23.0和gotip(devel go1.24-84e58c8 linux/amd64)跑的benchmark。

// 在experiments/swiss-table-map/mapbenchmark目录下
$go test -run='^$' -timeout=10h -bench=. -count=10 > origin-map.txt
$GOEXPERIMENT=swissmap gotip test -run='^$' -timeout=10h -bench=. -count=10 > swiss-table-map.txt
$benchstat origin-map.txt swiss-table-map.txt > result.txt

注:gotip版本的安装请参考《Go语言第一课》专栏的第3讲。benchstat安装命令为go install golang.org/x/perf/cmd/benchstat@latest

下面是result.txt中的结果:

goos: linux
goarch: amd64
pkg: demo
cpu: Intel(R) Xeon(R) Platinum
                                  │ origin-map.txt │         swiss-table-map.txt          │
                                  │     sec/op     │    sec/op     vs base                │
MapIter/Int/12-8                      179.7n ± 10%   190.6n ±  4%        ~ (p=0.436 n=10)
MapIter/Int/256-8                     4.328µ ±  5%   3.748µ ±  1%  -13.40% (p=0.000 n=10)
MapIter/Int/8192-8                    137.3µ ±  1%   123.6µ ±  1%   -9.95% (p=0.000 n=10)
MapAccessHit/Int64/12-8               10.12n ±  2%   10.68n ± 14%   +5.64% (p=0.000 n=10)
MapAccessHit/Int64/256-8              10.29n ±  3%   11.29n ±  1%   +9.77% (p=0.000 n=10)
MapAccessHit/Int64/8192-8             25.99n ±  1%   14.93n ±  1%  -42.57% (p=0.000 n=10)
MapAccessMiss/Int64/12-8              12.39n ± 88%   20.99n ± 50%        ~ (p=0.669 n=10)
MapAccessMiss/Int64/256-8             13.12n ±  6%   11.34n ±  7%  -13.56% (p=0.000 n=10)
MapAccessMiss/Int64/8192-8            15.71n ±  1%   14.03n ±  1%  -10.66% (p=0.000 n=10)
MapAssignGrow/Int64/12-8              607.1n ±  2%   622.6n ±  2%   +2.54% (p=0.000 n=10)
MapAssignGrow/Int64/256-8             25.98µ ±  3%   23.22µ ±  1%  -10.64% (p=0.000 n=10)
MapAssignGrow/Int64/8192-8            792.3µ ±  1%   844.1µ ±  1%   +6.54% (p=0.000 n=10)
MapAssignPreAllocate/Int64/12-8       450.2n ±  2%   409.2n ±  1%   -9.11% (p=0.000 n=10)
MapAssignPreAllocate/Int64/256-8     10.412µ ±  1%   6.055µ ±  2%  -41.84% (p=0.000 n=10)
MapAssignPreAllocate/Int64/8192-8     342.4µ ±  1%   232.6µ ±  2%  -32.05% (p=0.000 n=10)
MapAssignReuse/Int64/12-8             374.2n ±  1%   235.4n ±  2%  -37.07% (p=0.000 n=10)
MapAssignReuse/Int64/256-8            8.737µ ±  1%   4.716µ ±  4%  -46.03% (p=0.000 n=10)
MapAssignReuse/Int64/8192-8           296.4µ ±  1%   181.0µ ±  1%  -38.93% (p=0.000 n=10)
geomean                               1.159µ         984.2n        -15.11%

我们看到了除了少数测试项有不足外(比如MapAssignGrow以及一些元素数量少的情况下),大多数测试项中,新版基于swiss table的map的性能都有大幅提升,有些甚至接近50%!

5. 小结

本文探讨了Go语言中的map实现的重塑,即引入Swiss Table这一高效哈希表结构的背景与优势。Swiss Table由Google工程师开发,旨在优化内存使用和提升性能,解决了传统哈希表在高负载情况下的性能瓶颈。通过对比现有的链式哈希实现,Swiss Table展示了在查询、插入和删除操作上显著提高的性能,尤其是在处理大规模数据时。

经过两年多的实验与评估,Go团队决定将Swiss Table作为Go map的底层实现,预计将在Go 1.24中正式落地。新的实现不仅承继了原有的语义约束,还通过引入多表和渐进式扩容的设计,进一步优化了扩容过程的性能。尽管当前实现仍在完善中,但Swiss Table的引入无疑为Go语言的性能提升提供了新的可能性,并为未来进一步优化奠定了基础。

对于那些因Go引入自定义iterator而批评Go团队的Gopher来说,这个Go map的重塑无疑会很对他们的胃口。

本文涉及的源码可以在这里下载。

6. 参考资料


Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily
  • Gopher Daily Feed订阅 – https://gopherdaily.tonybai.com/feed

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Go团队的工作方式

本文永久链接 – https://tonybai.com/2024/06/22/how-things-get-done-on-the-go-team

Go 1.23版本即将发布(2024.8)之前,在GopherCon 2024开幕(2024.7)之前,Go团队成员Cameron Balahan(Go产品负责人)、 Sameer Ajmani(Go团队工程总监)和Russ Cox(Go团队技术负责人)参加了业界知名的播客栏目GoTime的最新一期活动,主题是“How things get done on the Go Team”。在这期活动中,Go团队这三个leader分享了Go团队的工作方式,包括:Go团队的组成、现状与职责划分、与社区互动、决策与规划流程、产品管理等方面。这里基于这期播客的脚本提炼了其中主要的观点,贴到这里供大家参考。

1. Go团队组成及职责划分

Go团队从2007年诞生,至今已经有17年了。最初的Go团队由罗伯·派克(Rob Pike)、罗伯特·格瑞史莫(Robert Griesemer)和肯·汤普森(Ken Thompson)三个Go语言之父组成。之后Russ Cox和Ian Lance Taylor加入团队,形成了Go团队最核心的五人组。

Sameer Ajmani在Go 1.0发布前后加入,当时团队有10几个人,我们熟悉的context包就是由他和Russ Cox一起设计并实现的。

Cameron Balahan在4年前加入Go团队,他也是今年在Google I/O大会上做“Go是一个平台”演讲的Go团队成员。

目前Google内部组织调整后,Go团队划归Google云团队管理,但其工作相对独立。 现在,Go团队由不同小组组成,主要包括三个小组:核心组、工具组和安全组。核心组负责编译器、运行时、链接器以及核心发布流程。工具组负责构建系统、Go命令、Go VSCode IDE插件以及gopls语言服务器等。安全组则专注于Go的供应链安全性漏洞扫描和修复等方面。

尽管划分了不同的小组,但Go团队在日常工作中感觉就像是一个整体,各小组之间合作紧密。特定任务往往需要几个小组共同参与,例如漏洞检测与修复功能的开发就涉及了核心组、工具组和安全组的工作。

Go团队的工作由核心成员和开源社区两部分组成。核心成员负责构建整体框架与关键功能,而开源社区则为Go语言贡献众多细节上的改进和完善。两者紧密互动,形成良性循环。

2. Go团队与Go社区的互动

Go社区对语言发展做出了重大贡献,因此Go团队始终采取非常积极开放的态度与社区互动。包括但不限于使用Slack、邮件列表、Issue跟踪、Go博客等多种渠道倾听Go社区声音,接纳Go社区贡献。任何人都可以参与讨论并提出建议。

目前较为正式的决策途径是“Go提案流程(Proposal Process)”。任何人都可以在这一平台上提出建议,供Go团队和全体社区评议。不论大小,只要通过审议,这些建议都可能被纳入语言或生态系统的未来发展规划。

除了直接参与讨论和决策外,Go社区还可以通过编写代码、发现并报告漏洞等方式为Go语言做贡献。Go团队会将高质量的外部代码整合进官方发行版。

3. 决策与规划流程

Go团队在做决策时,会优先考虑目标的一致性和充分的信息共享(比如公开利用Go遥测工具采集的数据)。如果出现分歧,通常是由于目标不一致或信息不对称(以类型别名加入Go的过程为例)造成的。因此,团队会先明确共同目标,并确保每个人掌握了相同的信息,然后再做出决策。

在规划过程中,Go团队首先要考虑Go语言既定的目标,即能够同时处理”生产规模化”(大量机器与海量数据)和”人力规模化”(大型项目与众多贡献者)。任何需要持续10年以上的重大决策,都必须符合这两个目标。

从长远来看,安全性与开源软件的可持续发展是Go团队需要重点关注的问题。他们将积极主导新标准与新模式,以提高整个行业的供应链安全性水平。

功能规划上,Go团队会同时考虑Go用户/社区和Google内部需求:Go用户和Go社区从Go中寻找价值,比如高生产力、高性能、高可靠和高安全;Google要确保其内部系统运行良好,开发人员满意,其系统可靠,安全,诸如此类。当然,Google也希望外部Go开发人员也这样做。同时,Google也希望那些外部的Go开发人员获得成功和快乐。为此,Go团队会寻求双赢解决方案。比如兼容性工作就是为了满足Kubernetes等重要系统的需求(IP地址解析)。在新特性开发过程中,Go团队会确保功能在整个生态链上保持一致性。

在发布规划上,Go团队需要考虑两个周期,一个是Go团队公开的Go版本发布周期,主版本一年两次。同时,Go团队leader还要考虑内部Google的规划周期,往往有一个年度计划周期,Go团队在其中执行 OKR、目标和关键结果。

4. 产品管理与Go的未来展望

作为Go产品负责人,CAMERON BALAHAN认为他会从优先级路线图、愿景角度以及Go团队为用户/社区和Google提供的价值的角度来弄清楚Go是什么,他认为Go是用于开发生产级软件的高效平台。作为编程语言,Go语言的产品管理理念就是构建一个高效且稳定的平台,支撑”生产级软件的高效开发”。

Go在解决云问题方面非常成功。云的大部分基础设施都是用Go编写的,并且Go在这方面做得很好,具有独特优势。Go团队希望Go在这一领域能够提供持续性的方案并取得持续性的成功,这决定了Go团队关注两个核心要素:生产效率和软件质量,这其中包括可靠性、安全性等重要的要素。

此外,Sameer认为人工智能的发展也为Go带来了新的机遇,随着越来越多的大公司、企业和初创公司希望在人工智能模型之上构建系统,而如何使Go成为构建智能基础设施以及基于大模型构建生产级、值得信赖、可靠的AI应用系统的语言,是下一个重要的前沿领域,Go团队将看到对此的大量需求,并认为Go是一个非常合适的选择。Go团队也在拭目以待!

编程语言的采用是一个缓慢的过程。Go语言目前已经到了一个关键的增长点,有望在新兴计算领域(AI)获得更广泛的使用。团队需要持续关注新的计算范式,及时调整以满足新需求。

Go社区对该语言的热爱是Go发展的重要动力。整个Go社区都是建立在Go之上的。Go团队本身无法建造所有东西,Go团队只要确保Go用户能够使用Go构建他们需要构建的东西,积极赋能社区发挥创造力,丰富Go的生态系统,才能继续让Go保持在人们需要的那种前沿,以便建立他们的业务、构建软件、构建他们需要的东西,生产级的高效、安全与可靠。

5. 不受欢迎的观点(GoTime常设环节)

  • Sameer:context is fine。
  • Cameron:I really like Go’s error handling.
  • Russ:null pointers are fine. They’re kind of a fundamental fact of computers, is that memory can be zeroed.

Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats