不同架构的原子操作指令是不同的

不同架构的原子操作指令是不同的

Content #

因为不同的 CPU 架构甚至不同的版本提供的原子操作的指令是不同的,所以,要用一种编程语言实现支持不同架构的原子操作是相当有难度的。不过,还好这些都不需要你操心,因为 Go 提供了一个通用的原子操作的 API,将更底层的不同的架构下的实现封装成 atomic 包,提供了修改类型的原子操作(atomic read-modify-write,RMW)和加载存储类型的原子操作(Load 和 Store)的 API。

有的代码也会因为架构的不同而不同。有时看起来貌似一个操作是原子操作,但实际上,对于不同的架构来说,情况是不一样的。比如下面的代码的第 4 行,是将一个 64 位的值赋值给变量 i:

const x int64 = 1 + 1<<33

func main() {
    var i = x
    _ = i
}

如果你使用 GOARCH=386 的架构去编译这段代码,那么,第 5 行其实是被拆成了两个指令,分别操作低 32 位和高 32 位。反编译指令:

> GOARCH=386 go tool compile -N -l test.go
> GOARCH=386 go tool objdump -gnu test.o
  main.go:5             0x37b                   83ec08                  SUBL $0x8, SP                        // sub $0x8,%esp
  main.go:6             0x37e                   c7042401000000          MOVL $0x1, 0(SP)                     // movl $0x1,(%esp)
  main.go:6             0x385                   c744240402000000        MOVL $0x2, 0x4(SP)                   // movl $0x2,0x4(%esp)
  main.go:8             0x38d                   83c408                  ADDL $0x8, SP                        // add $0x8,%esp

如果 GOARCH=amd64 的架构去编译这段代码,那么,第 5 行其中的赋值操作其实是一条指令:

❯ GOARCH=amd64 go tool compile -N -l main.go
❯ GOARCH=amd64 go tool objdump -gnu main.o
  main.go:5             0x2fd                   4883ec10                SUBQ $0x10, SP                       // sub $0x10,%rsp
  main.go:5             0x301                   48896c2408              MOVQ BP, 0x8(SP)                     // mov %rbp,0x8(%rsp)
  main.go:5             0x306                   488d6c2408              LEAQ 0x8(SP), BP                     // lea 0x8(%rsp),%rbp
  main.go:6             0x30b                   48b80100000002000000    MOVQ $0x200000001, AX                // mov $0x200000001,%rax
  main.go:6             0x315                   48890424                MOVQ AX, 0(SP)                       // mov %rax,(%rsp)
  main.go:8             0x319                   488b6c2408              MOVQ 0x8(SP), BP                     // mov 0x8(%rsp),%rbp
  main.go:8             0x31e                   4883c410                ADDQ $0x10, SP                       // add $0x10,%rsp
  main.go:8             0x322                   c3                      RET                                  // retq

所以,如果要想保证原子操作,切记一定要使用 atomic 提供的方法。

Viewpoints #

From #

12 | atomic:要保证原子操作,一定要使用这几种方法