为什么Go不显示内存重新排序?

时间:2013-11-11 08:12:20

标签: c++ memory concurrency go

我正在阅读preshing的博客Memory Reordering Caught in the Act,并通过他的example code

重现内存重新排序

然后我想知道我是否可以通过Go重现内存重新排序,所以我在go中编写了示例代码,但是在Go中没有显示内存重新排序。

我写信是为了分享一些调查结果。

你能帮忙解释为什么Go无法重新排序吗?感谢。

Go中的示例代码:

 package main

    import (
            "fmt"
            "math/rand"
    )

    var x, y, r1, r2 int
    var detected = 0

    func randWait() {
            for rand.Intn(8) != 0 {
            }
    }

    func main() {
            beginSig1 := make(chan bool, 1)
            beginSig2 := make(chan bool, 1)
            endSig1 := make(chan bool, 1)
            endSig2 := make(chan bool, 1)
            go func() {
                    for {
                            <-beginSig1
                            randWait()
                            x = 1
                            r1 = y
                            endSig1 <- true
                    }
            }()
            go func() {
                    for {
                            <-beginSig2
                            randWait()
                            y = 1
                            r2 = x
                            endSig2 <- true
                    }
            }()
            for i := 1; ; i = i + 1 {
                    x = 0
                    y = 0
                    beginSig1 <- true
                    beginSig2 <- true
                    <-endSig1
                    <-endSig2
                    if r1 == 0 && r2 == 0 {
                            detected = detected + 1
                            fmt.Println(detected, "reorders detected after ", i, "iterations")
                    }
            }
    } 

汇编代码(“ndisasm -b 32”)在C ++与Go

之间显示不同
  • 来自C ++的汇编代码

    00000CF0  C705520300000100  mov dword [0x352],0x1     //X=1
             -0000
    00000CFA  8B0550030000      mov eax,[0x350]     
    00000D00  89054E030000      mov [0x34e],eax      //r1=Y
    
  • Go的汇编代码

    000013EA  48                dec eax
    000013EB  C70425787F170001  mov dword [0x177f78],0x1     //x=1
             -000000
    000013F6  48                dec eax
    000013F7  8B1C25807F1700    mov ebx,[0x177f80]
    000013FE  48                dec eax
    000013FF  891C25687F1700    mov [0x177f68],ebx          //r1=Y
    00001406  48                dec eax
    

Go似乎围绕共享内存的访问使用dec eax,但dec eax无法阻止内存重新排序

  1. Intel® 64 and IA-32 Architectures Software Developer Manuals第3卷第8.2节显示了可能阻止内存重新排序的情况,但未包含dec eax ...

  2. 我尝试在C代码中添加dec eax作为共享内存访问的边距,内存重新排序仍在那里。

  3. 到现在为止,我不知道原因。请帮帮我,谢谢。

3 个答案:

答案 0 :(得分:8)

我没有看到在任何地方设置GOMAXPROC的电话?如果你没有打电话,你将只在一个CPU上运行,它不会显示重新排序:http://golang.org/pkg/runtime/#GOMAXPROCS

更新:在Go 1.5(已发布2015/08/19)及更高版本中,您不再需要设置GOMAXPROCS - Go默认使用所有CPU。

答案 1 :(得分:0)

Go内存模型不是C或C ++的模型。

查看http://golang.org/ref/mem,其中描述了“Happens Before”HB关系及其与渠道的关系。请注意,当前实现可能具有比内存模型所需的更多的HB。

答案 2 :(得分:0)

我相信mov ebx,[0x177f80]指令会有所不同。

它加载ebx,这意味着mov [0x177f68],ebx依赖于它,并且不能在它之前移动。因此,如果重新排序,则使用ebx的两个移动必须一起重新排序。我认为这是不允许的 - x86_64架构不会重新排序其他读取的读取(不是100%肯定)。