我正在阅读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
无法阻止内存重新排序
Intel® 64 and IA-32 Architectures Software Developer Manuals第3卷第8.2节显示了可能阻止内存重新排序的情况,但未包含dec eax
...
我尝试在C代码中添加dec eax
作为共享内存访问的边距,内存重新排序仍在那里。
到现在为止,我不知道原因。请帮帮我,谢谢。
答案 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%肯定)。