Почему Go не показывает переупорядочение памяти?

Я читаю пресессионный блог Переупорядочение памяти, замеченное в законе, и воспроизведенное переупорядочение памяти по его примеру кода

Тогда мне интересно, могу ли я воспроизвести переупорядочение памяти 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" ) отличается от С++ vs Go

  • Код сборки из С++

    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

Я не вижу вызова для установки GOMAXPROC в любом месте? Если вы его не назовете, вы будете работать только на одном CPU, который никогда не отобразит повторный порядок: http://golang.org/pkg/runtime/#GOMAXPROCS

Обновление: в Go 1.5 (выпущено 2015/08/19), а позже вам больше не нужно устанавливать GOMAXPROCS - Go по умолчанию для использования всех ваших процессоров.

Ответ 2

Модель памяти Go не относится к C или С++.

Посмотрите http://golang.org/ref/mem, в котором описывается отношение Happens Before" HB и как оно относится к каналам. Обратите внимание, что текущие реализации могут иметь больше HB, чем требуется для модели памяти.

Ответ 3

Я считаю, что инструкция mov ebx,[0x177f80] делает разницу.

Он загружает ebx, что означает, что mov [0x177f68],ebx зависит от него и не может быть перемещен впереди него. Поэтому, если переупорядочение, оба перемещения, которые используют ebx, должны быть переупорядочены вместе. Я думаю, что это не разрешено - архитектор x86_64 не переупорядочивает чтения с другими чтениями (на 100% не уверен).