1

鑑於x86總存儲順序和Java存儲器模型中發生的事前關係,我們知道編譯器不保證指令執行的順序。它可以根據需要重新排序,以提高性能。鑑於此,我們有:Java內存模型和併發

  • EAXEBX是寄存器
  • [x][y]是內存位置
  • r1r2是局部變量的名稱
  • xy共享變量訪問的名字所有線程。所有變量都是32位整數。
  • 不,這是不是一個家庭問題

所以我有兩套的問題,我想,以確定可能的輸出:

[x] == [y] == 0 // the address space of [x] and [y] are 0. 

// Thread 1       Thread 2 
MOV [x] <- 1      MOV [y] <- 1 
MOV EAX <- [y]      MOV EBX <- [x] 

這對寄存器EBXEAX的可能值?

int x = 0; 
int y = 0; 

// Thread 1       Thread 2 
x = 1;        y = 1; 
r1 = y;        r2 = x; 

什麼r1r2的可能值?

+1

IMO,您應該將x86架構與Java分離。這是一個關於JVM被允許做什麼的問題,或者這是一個關於x86處理器實際上可以做什麼的問題。至於JVM被允許做什麼,線程1可以將0或1分配給'r1',而線程2可以將0或1分配給r2。 –

回答

4

32-bit integer保證是atomicJVM,所以這不是一個問題。

您有2個變量x和y在線程之間共享,沒有synchronization

  1. Thread1突變x並讀取y。
  2. Thread2突變y並讀取x。

因此,thread1可以看到y的陳舊值(1或0),並且thread2可以看到陳舊值x(1,0)。

這意味着你可以得到的(EAX,EBX)所有四種可能的組合: (0,0) (0,1) (1,0) (1,1)

+1

你的答案的內容是好的,但我覺得對普通單詞使用'代碼格式化'是不必要的... – Nayuki

+0

@NayukiMinase我不是一個SO專家,我使用「代碼格式化」突出重要單詞,隨意編輯:) –

+1

不是像你的那樣得分的專家嗎?確實很謙虛。無論如何,既然你回答了,我會把編輯留給你的判斷。 – Nayuki

2

我們可以簡單的標籤說明如下:

A) [x] <- 1   C) [y] <- 1 

B) EAX <- [y]   D) EBX <- [x] 

我們知道,A B之前到來,和C自帶d之前,所以只需插入C和d爲AB在所有可能的排列:

CDAB 
CADB 
CABD 
ACDB 
ACBD 
ABCD 

,並考慮各種可能性的影響,並指出,多數開始要麼ACCA,輸出(EAX,EBX)=(1,1)因爲分配是EAX和之前發生正在設置。剩下的就是檢查其他兩種可能性。 CDAB給出(EAX,EBX)=(1,0)ABCD給出(EAX,EBX)=(0,1)

對於Java版本,您聲明編譯器不保證執行語句的順序。在這種情況下,訂購A,B,CD以獲得(0,0),(1,0),(0,1)和(1,1)並不困難。

+0

第一個問題呢? – cybertextron

+0

@philippe第一個問題是剛纔用匯編寫的同樣的問題。問題編寫者我假設用r1表示「寄存器1」或EAX,而r2表示「寄存器2」或EBX。 –

+0

我們知道A在B之前嗎?我不認爲Java提供這種保證。從線程1的角度來看,A和B是獨立的,在這種情況下,重新排序指令是可行的。 – zapl

4

86有一個強烈排序的內存模型,但仍然允許StoreLoad reordering

Jeff Preshing的博客文章:Memory Reordering Caught in the Act,恰好使用那對商店 - 然後加載序列作爲測試用例來證明重新排序確實可以在真實硬件上觀察到。他有源代碼和一切。

請注意,每個線程都有自己的架構狀態(包括所有的寄存器)。所以thread1的EAX與thread2的EAX不同。在thread2中使用EBX使得它更容易討論,與發生什麼事情的POV沒什麼不同。

無論如何,兩個寄存器都可以以0結束。這很少發生,但它可以,因爲每個線程的存儲可以被延遲(在存儲緩衝區或其他任何地方),直到其他線程的加載選擇了一個值之後。這是合法的,CPU可以積極地使用預取的數據來滿足負載,並緩衝存儲,以便它們在退休時不會立即全局可見。 (「退休」是指運行指令的線程的體系結構狀態(包括EIP)已轉移到下一條指令,並且提交了效果。)

其他可能性,一旦塵埃落定,總是包括全局變量是1。在每個線程的寄存器中,所有4個可能的0和1值都是可能的,包括1。他們有可能看到彼此的商店。我不確定這有多可能;它可能需要一個線程在其存儲之後但在其加載之前被中斷。如果兩個線程都在同一個物理內核上運行(超線程),則爲this possibility is much more likely


即使對於xy存儲是不對齊和跨越高速緩存線,和01是唯一可能的值。 (C編譯器輸出和JVMs會將變量與它們的自然對齊對齊,這使得它不成問題,但是你可以在asm中做任何你想做的事情,所以我想我會提到它)。這是因爲這兩個值只有不同在最低有效字節中。

如果你存儲一個32位的跨越兩個高速緩存行-1爲4個字節,其它線程可能(取決於高速緩存行邊界在何處)加載的0x00ffffff0xff0000000x0000ffff0xffff0000等的值,以及通常的00xffffffff(又名-1)。


回覆:Java。我沒有閱讀Java內存模型。其他答案是說它甚至允許編譯時重新排序(如c++11's std::atomic rules)。即使不是,沒有完整的內存屏障,StoreLoad重新排序也會發生。所以所有四個結果都是可能的

即使您的JVM運行在x86 CPU上(而不是像ARM這樣微弱有序的硬件),情況也是如此。

This answer to another question可能會說明爲什麼在x86上存在LFENCE/SFENCE,儘管它們在大多數情況下都是無操作的。 (即,當不使用movnt或弱排序存儲區域(如USWC視頻存儲器))。

或者,您可以閱讀Jeff Preshing的其他博客文章,以瞭解更多關於內存訂購的信息。我發現它真的幫助我自己。