【问题标题】:Why Go doesn't show memory-reordering?为什么 Go 不显示内存重新排序?
【发布时间】:2013-11-11 08:12:19
【问题描述】:

我正在阅读 preshing 的博客 Memory Reordering Caught in the Act,并通过他的 example code 复制了内存重新排序

然后我想知道我是否可以通过 Go 重现 memory-reordering,所以我在 go 中编写了示例代码,但是 memory-reordering 在 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 作为共享内存访问的边距,内存重新排序仍然存在。

到目前为止,我不知道原因。请帮我解决一下,谢谢。

【问题讨论】:

    标签: c++ memory concurrency go


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢你,@尼克。我错过了 GOMAXPROC...在添加 GOMAXPROCS 后进行内存重新排序。
    【解决方案2】:

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

    查看http://golang.org/ref/mem,它描述了“发生在之前”的 HB 关系以及它与渠道的关系。请注意,当前实现的 HB 可能比内存模型所需的多。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 谢谢你,@ugoren。您是正确的,读取没有与 x86 架构中的其他读取重新排序。这里,重新排序在mov dword [0x177f78],0x1 mov [0x177f68],ebx 之间,一读一写。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-27
      • 2020-03-31
      • 2021-12-02
      相关资源
      最近更新 更多