为什么会有atomic.LoadInt32

Go语言精选

共 2437字,需浏览 5分钟

 ·

2022-06-09 05:38

前些天我们聊了 Golang 内存对齐[1]的话题,后来我突然想到另一个问题:为什么会有 atomic.LoadInt32[2]?可能你觉得思维太跳跃了,容我慢慢道来:首先,有 atomic.LoadInt64[3] 很正常,因为对一个 int64 来说,它的大小是 8 个字节,如果是 32 位平台的话(字长 4 字节),CPU 一次最多操作 4 个字节,需要两次才能拿到全部数据,所以封装一个 atomic.LoadInt64 来实现原子操作;但是,对一个 int32 数据来说,它的大小是 4 字节,不管是 32 位平台(字长 4 字节),还是 64 位平台(字长 8 字节),CPU 应该都可以保证一次操作拿到数据,换句话说,如果读取一个 int32 数据,那么本身就应该是原子的,可是为什么会有 atomic.LoadInt32,这不是脱了裤子放屁么?

有病没病走两步,让我们写一段代码来验证一下:

package main

import "sync/atomic"

var v = int32(0)

func main() {
 var x int32
 x = v // main.go:9
 _ = x
 x = atomic.LoadInt32(&v) // main.go:11
 _ = x
}

通过「go tool compile」运行代码,拿到对应的汇编结果:

shell> go tool compile -N -l -S main.go

0x0016 00022 (main.go:9)        MOVL    "".v(SB), AX
0x001c 00028 (main.go:9)        MOVL    AX, "".t+4(SP)
0x0020 00032 (main.go:11)       MOVL    "".v(SB), AX
0x0026 00038 (main.go:11)       MOVL    AX, "".t+4(SP)

不管是「x = v」还是「x = atomic.LoadInt32(&v)」,对应的汇编结果一摸一样。问题越来越有趣了,让我们看看是否能从 sync/atomic[4] 的源代码中找到答案:

Golang 代码中只有函数声明,实际上是使用汇编实现的:

// doc.go
func LoadInt32(addr *int32) (val int32)

// asm.s
TEXT ·LoadInt32(SB),NOSPLIT,$0
 JMP runtime∕internal∕atomic·Load(SB)

顺着路径,跳转到 runtime/internal/atomic[5],会发现每个平台都有独立的 Load 实现:

在 amd64 平台,Load 是用 Golang 实现的,等价于直接读取:

func Load(ptr *uint32) uint32 {
 return *ptr
}

在 arm64 平台,Load 是用汇编实现的,并不是简单的一次操作:

TEXT ·Load(SB),NOSPLIT,$0-12
 MOVD ptr+0(FP), R0
 LDARW (R0), R0
 MOVW R0, ret+8(FP)
 RET

如上可见,atomic.LoadInt32 之所以存在,是因为某些平台存在特殊性,所以我们需要封装一个统一的操作,如此更有利于我们写出平台无关的代码。

本文仅讨论了 atomic 的原子性[6],实际上它还保证了可见性[7]有序性[8],有兴趣的朋友可以搜索内存屏障相关内容,这是一个很复杂的主题,我就不献丑了,推荐阅读:Golang Memory Model[9]

参考资料

[1]

Golang 内存对齐: https://blog.huoding.com/2021/09/29/951

[2]

atomic.LoadInt32: https://pkg.go.dev/github.com/hslam/atomic#LoadInt32

[3]

atomic.LoadInt64: https://pkg.go.dev/github.com/hslam/atomic#LoadInt64

[4]

sync/atomic: https://github.com/golang/go/tree/master/src/sync/atomic

[5]

runtime/internal/atomic: https://github.com/golang/go/tree/master/src/runtime/internal/atomic

[6]

原子性: https://www.1024cores.net/home/lock-free-algorithms/so-what-is-a-memory-model-and-how-to-cook-it

[7]

可见性: https://www.1024cores.net/home/lock-free-algorithms/so-what-is-a-memory-model-and-how-to-cook-it/visibility

[8]

有序性: https://www.1024cores.net/home/lock-free-algorithms/so-what-is-a-memory-model-and-how-to-cook-it/ordering

[9]

Golang Memory Model: https://www.jianshu.com/p/1596e1d7c126



推荐阅读


福利

我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。

浏览 63
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报