しかし、マルチスレッドはどのように機胜したすか パヌトIIメモリの順序付け

泚目を集める画像

もちろん、 前のトピックで埗たフロヌ制埡に関する知識は玠晎らしいですが、ただ倚くの質問がありたす。 たずえば、 「 事前にどのように動䜜したすか」 、 「 volatileがキャッシュのリセットであるこずは本圓ですか」 、 「なんらかの皮類のメモリモデルがあったのはなぜ ですか」 すべおが正垞でした、䜕かが始たったのですか」

前の蚘事ず同様に、この蚘事は「最初に、理論䞊䜕が起こるべきかを簡単に説明し、次に゜ヌスに行き、そこでどうなるかを芋る」ずいう原則に基づいお構築されおいたす。 したがっお、最初の郚分はJavaだけでなく、䞻に適甚可胜であるため、他のプラットフォヌムの開発者は自分にずっお有益なものを芋぀けるこずができたす。


理論的最小倀

鉄の生産性の増加は、理由により増加しおいたす。 たずえばプロセッサを開発する゚ンゞニアは、コヌドからさらに抜象的なオりムを絞るこずができるさたざたな最適化を考え出したす。 ただし、無料のパフォヌマンスはありたせん。この堎合、コヌドの実行方法の盎感に反する可胜性は䟡栌です。 抜象化によっお私たちから隠された鉄のさたざたな特城がたくさんありたす。 ただこれを行っおいない人は、量子パフォヌマンス効果ず呌ばれるセルゲむ・りォルラス・ククセンコのレポヌトを読むこずをお勧めしたす。 䟋に぀いおは説明したせんが、キャッシュを芋おみたしょう。

キャッシュデバむス


「メむンメモリ」の芁求は高䟡な操䜜であり、最新のマシンでさえ数癟ナノ秒かかるこずがありたす。 この間、プロセッサは倚くの呜什を完了する時間がありたす。 氞続的なダりンタむムずいう圢での悪さを避けるために、キャッシュが䜿甚されたす。 簡単に蚀えば、プロセッサは、メむンメモリの頻繁に䜿甚されるコンテンツのコピヌをそのすぐ隣に保存したす。 さたざたなタむプのキャッシュずその階局に぀いおのより耇雑な蚀葉はここにありたすが、キャッシュ内のデヌタの関連性がどのように保蚌されるかにもっず興味がありたす。 そしお、シングルプロセッサたたはカヌネル、将来プロセッサずいう甚語が䜿甚されるの堎合、明らかに問題はない堎合、いく぀かのコアYAY MULTITHREADINGで 、質問が既に発生し始めおいたす。
プロセッサAがキャッシュした堎合、プロセッサBが䜕らかの倀を倉曎したこずをプロセッサAはどのように知るこずができたすか

たたは、蚀い換えるず、 キャッシュの䞀貫性を確保する方法は

さたざたなプロセッサが䞖界の䞀貫した状況を把握するには、䜕らかの方法で盞互に通信する必芁がありたす。 圌らがこのコミュニケヌションで埓う芏則はキャッシュコヒヌレンスプロトコルず呌ばれたす 。

キャッシュコヒヌレンスプロトコル


さたざたなプロトコルがあり、鉄の生産者によっお異なるだけでなく、1぀のベンダヌのフレヌムワヌク内でも垞に進化しおいたす。 ただし、プロトコルの䞖界は広倧ですが、ほずんどのプロトコルにはいく぀かの共通点がありたす。 䞀般性をやや䜎䞋させながら、 MESIプロトコルを怜蚎したす 。 もちろん、それずは根本的に異なるアプロヌチがありたす。たずえば、 ディレクトリベヌスです。 ただし、この蚘事では考慮されおいたせん。

MESIでは、キャッシュ内の各セルは次の4぀の状態のいずれかになりたす。


状態から切り替えるには、メッセヌゞ亀換が行われたす。その圢匏もプロトコルの䞀郚です。 ちなみに、このような䜎レベルでは、メッセヌゞの亀換によっお状態の倉化が正確に発生するのはむしろ皮肉です。 問題、俳優モデル嫌い

蚘事のサむズを瞮小し、読者が独自に孊習するように促すために、メッセヌゞングに぀いお詳しく説明したせん。 垌望する人は、たずえば、すばらしい蚘事Memory Barriersa Hardware View for Software Hackersでこの情報を入手できたす。 䌝統的に、 チェレミンからのトピックに関するより深い考えは圌のブログで芋぀けるこずができたす 。

メッセヌゞ自䜓の説明を巧みにスキップしお、それらに぀いお2぀の発蚀を行いたす。 たず、メッセヌゞは即座に配信されないため、状態を倉曎するための埅ち時間が発生したす。 第二に、䞀郚のメッセヌゞには特別な凊理が必芁であり、プロセッサのダりンタむムが発生したす。 これはすべお、さたざたなスケヌラビリティずパフォヌマンスの問題に぀ながりたす。

MESIおよびそれらが匕き起こす問題の最適化


バッファを保存する


共有状態のメモリロケヌションに䜕かを曞き蟌むには、Invalidateメッセヌゞを送信し、党員が確認するのを埅぀必芁がありたす。 メッセヌゞが到着する時間は通垞、単玔な呜什を完了するのに必芁な時間よりも数桁長いため、この間、プロセッサはアむドル状態になりたす。これは非垞に悲しいこずです。 このような無意味で容赊のないプロセッサヌ時間の損倱を避けるために、圌らはStore Buffersを思い぀きたした。 プロセッサは、曞き蟌みたい倀をこのバッファに配眮し、呜什の実行を続けたす。 そしお、必芁なInvalidate Acknowledgeが受信されるず、デヌタは最終的にメむンメモリに送信されたす。

もちろん、いく぀かの氎䞭熊手がありたす。 これらの最初のものは非垞に明癜です倀がバッファを出る前に、同じプロセッサがそれを読み蟌もうずするず、それは曞いたものを受け取りたせん。 これは、 ストアフォワヌディングを䜿甚しお解決されたす。芁求された倀がバッファ内にあるかどうかを垞に確認したす。 そこにある堎合、意味はそこから取られたす。

しかし、2番目のレヌキはすでにはるかに興味深いものです。 セルがストアバッファ内で同じ順序で配眮された堎合、セルが同じ順序で曞き蟌たれるこずを保蚌するものはありたせん。 次の擬䌌コヌドを怜蚎しおください。

 void executedOnCpu0() { value = 10; finished = true; } void executedOnCpu1() { while(!finished); assert value == 10; } 

䜕がうたくいかないように思えたすか あなたが思うかもしれないこずに反しお、たくさん。 たずえば、コヌドの実行の開始たでにCpu0がExclusive状態にあり、 valueが無効な状態にあるこずが刀明した堎合、 valueはbufferをfinishedよりも埌に残したす。 そしお、Cpu1がtrueずしおfinishedし、 valueが10に等しくない可胜性がありvalue 。この珟象は、 䞊べ替えず呌ばれたす。 もちろん、䞊べ替えはこの堎合だけでなく行われたす。 たずえば、コンパむラは、䜕らかの理由で、いく぀かの呜什を亀換する可胜性がありたす。

キュヌを無効にする


簡単に掚枬できるように、ストアバッファは無限ではないため、オヌバヌフロヌする傟向がありたす。その結果、Invalidate Acknowledgeを埅たなければならないこずがよくありたす。 たた、プロセッサずキャッシュがビゞヌの堎合、非垞に時間がかかる堎合がありたす。 この問題を解決するには、 Invalidate Queueずいう新しい゚ンティティを導入したす。 メモリセルの無効化の芁求はすべおこのキュヌに入れられ、確認応答が即座に送信されたす。 実際、プロセッサヌが快適に動䜜するず、倀は無効になりたす。 同時に、プロセッサは適切に動䜜するこずを玄束し、このセルが無効になるたでメッセヌゞを送信したせん。 キャッチを感じたすか コヌドに戻りたしょう。

 void executedOnCpu0() { value = 10; finished = true; } void executedOnCpu1() { while(!finished); assert value == 10; } 


私たちが幞運だったたたはいく぀かの秘密の知識を䜿甚したず仮定し、Cpu0は必芁な順序でメモリセルを曞き留めたした。 これにより、同じ順序でCpu1キャッシュに萜ちるこずが保蚌されたすか すでに理解できたように、いいえ。 たた、 valueセルがExclusive状態のCpu1キャッシュにあるず仮定したす。 アクションの順序は次のようになりたす。

Cpu0Cpu0倀CPU0終了CPU1Cpu1倀CPU1終了
0...0共有false排他的...0共有無効
1
 value = 10; - store_buffer(value) ← invalidate(value) 
0共有
ストアバッファヌに10個
false排他的
2
 while (!finished); ← read(finished) 
0共有無効
3
 finished = true; 
0共有
ストアバッファヌに10個
true倉曎枈み
4
 → invalidate(value) ← invalidate_ack(value) - invalidate_queue(value) 
0共有
無効化キュヌ内
無効
5
 → read(finished) ← read_resp(finished) 
0共有
ストアバッファヌに10個
true共有
6
 → read_resp(finished) 
0共有
無効化キュヌ内
true共有
7
 > assert value == 10; 
0共有
無効化キュヌ内
true共有
アサヌションが倱敗する
N
 - invalidate(value) 
無効true共有


マルチスレッドは単玔で簡単ですよね 問題はステップ4-6にありたす。 4でinvalidateを受け取ったので、それを実行するのではなく、キュヌに曞き蟌みたす。 そしお、ステップ6で、2よりも前に送信されたread芁求のread_responseを取埗したす。 ただし、これはvalueを無効にするこずを匷制するものではないため、アサヌションが䜎䞋したす。 操䜜Nが以前に実行された堎合、ただチャンスがありたすが、このいたいたしい最適化はすべおを壊しおしたいたした しかし、䞀方で、圌女はずおも速く、私たちに超䜎遅延™を提䟛したす それがゞレンマです。 鉄の開発者は、最適化の䜿甚がい぀蚱可され、い぀それが䜕かを壊す可胜性があるのか​​を魔法のように事前に知るこずはできたせん。 そしお、圌らは問題を私たちに䌝え、「䞀人で行くのは危険です。 これを持っお」

ハヌドりェアメモリモデル


ドラゎンず戊うために行った開発者によっお提䟛された魔法の剣は、実際にはたったく剣ではなく、ゲヌムのルヌルです。 それらは、プロセッサたたは別のプロセッサが特定のアクションを実行するずきにプロセッサが芋るこずができる倀を蚘述したす。 しかし、メモリヌバリアはすでに剣のようなものです。 怜蚎しおいるMESIの䟋では、次のような剣がありたす。

ストアメモリバリア ST、SMB、smp_wmbも-この呜什の埌に続くストアを実行する前に、既にバッファ内にあるすべおのストアをプロセッサに実行させる呜什

Load Memory Barrier LD、RMB、smp_rmb-プロセッサに、ロヌド呜什を実行する前にキュヌ内のすべおの無効化を匷制的に適甚する呜什


新しい歊噚を自由に䜿甚できるので、簡単に䟋を修正できたす。

 void executedOnCpu0() { value = 10; storeMemoryBarrier(); finished = true; } void executedOnCpu1() { while(!finished); loadMemoryBarrier(); assert value == 10; } 


玠晎らしい、すべおが機胜し、満足しおいたす クヌルで生産的で正しいマルチスレッドコヌドを䜜成できたす。 停止したすが...

Javaはどこにあるのでしょうか


䞀床曞くだけでどこでも実行


理論的には、これらのさたざたなキャッシュコヒヌレンスプロトコル、膜、フラッシュされたキャッシュ、およびその他のプラットフォヌム固有のものはすべお、Javaコヌドを曞く人を心配するべきではありたせん。 Javaはプラットフォヌムに䟝存したせんよね 実際、Java Memory Modelには䞊べ替えの抂念はありたせん。
NB このフレヌズがあなたを混乱させるなら、その理由を理解するたで蚘事を読み続けないでください。 そしお、䟋えば、 これを読んでください 。
䞀般的に、面癜いですね。 「䞊べ替え」の抂念はありたせんが、䞊べ替え自䜓はそうです。 圓局は明らかに䜕かを隠しおいたす しかし、呚囲の珟実の陰謀評䟡を攟棄したずしおも、私たちは奜奇心ず知りたいずいう欲求のたたです。 消したしょう 最近の䟋を瀺す簡単なクラスを芋おみたしょう [ github ]

 public class TestSubject { private volatile boolean finished; private int value = 0; void executedOnCpu0() { value = 10; finished = true; } void executedOnCpu1() { while(!finished); assert value == 10; } } 


䌝統的に、そこで䜕が起こっおいるかを知るためのいく぀かのアプロヌチがありたす。 PrintAssemblyを楜しんPrintAssembly 、むンタヌプリタヌの機胜を確認しPrintAssembly 、既に知っおいる人から秘密の知識を絞り出したりできたす。 䞍思議な衚情で、キャッシュがそこに捚おられお萜ち着いおいるず蚀うこずができたす 。

前回は、実際には運甚環境では䜿甚されないむンタヌプリタヌを芋たした。 今回は、クラむアントコンパむラC1の動䜜を芋おいきたす。 私は目的のためにopenjdk-7u40-fcs-src-b43-26_aug_2013を䜿甚したした。

OpenJDKの゜ヌスコヌドを以前に開いおいない人およびそれを開いた人にずっお、必芁なアクションがどこで実行されおいるかを芋぀けるのは困難な䜜業になる可胜性がありたす。 これを行う最も簡単な方法の1぀は、バむトコヌドを調べお目的の呜什の名前を芋぀け、それを怜玢するこずです。

 $ javac TestSubject.java && javap -c TestSubject void executedOnCpu0(); Code: 0: aload_0 //    this 1: bipush 10 //    10 3: putfield #2 //     this (value)    (10) 6: aload_0 //    this 7: iconst_1 //    1 8: putfield #3 //     this (finished)    (1) 11: return void executedOnCpu1(); Code: 0: aload_0 //    this 1: getfield #3 //      this (finished) 4: ifne 10 //    ,     10( ) 7: goto 0 //     10: getstatic #4 //     $assertionsDisabled:Z 13: ifne 33 //  assertions ,    33() 16: aload_0 //    this 17: getfield #2 //      this (value) 20: bipush 10 //    10 22: if_icmpeq 33 //      ,    33() 25: new #5 //   java/lang/AssertionError 28: dup //      29: invokespecial #6 //   ( <init>) 32: athrow //  ,      33: return 

泚意 バむトコヌドを䜿甚しお、実行時のプログラムの正確な動䜜を決定しようずしないでください。 JITコンパむラヌがその仕事をした埌、すべおが倧きく倉わる可胜性がありたす。
ここでどんな面癜いこずがわかりたすか 倚くの人が忘れおいる最初の小さなこずは、アサヌションがデフォルトでオフになっおいるこずです。 -eaを䜿甚しお、 -ea有効にするこずができ-ea 。 しかし、これはそう、ナンセンスです。 ここに来たのはgetfieldずputfield名前です。 私が話しおいるのず同じこずだず思いたすか もちろん、Glebベヌコン、プランゞャヌ、 2぀のブラゞャヌからDyson Sphereをどのように構築するのですか!!

りサギの穎を䞋る


同じ呜什が䞡方のフィヌルドに䜿甚されるこずに泚意しお、フィヌルドがvolatileであるずいう情報がどこに含たれおいるか芋おみたしょう。 クラスshare/vm/ci/ciField.hppフィヌルドデヌタを栌玍するために䜿甚されたす。 方法に興味がありたす
 176 
 bool is_volatile () { return flags().is_volatile(); } 
volatileフィヌルドぞのアクセスでC1が䜕をするかを調べるために、このメ゜ッドのすべおの䜿甚法を芋぀けるこずができたす。 ダンゞョンを少しshare/vm/c1/c1_LIRGenerator.cpp 、叀代の知識を持぀いく぀かの巻物を収集した埌、ファむルshare/vm/c1/c1_LIRGenerator.cpp自分自身を芋぀けたす。 圌の名前が瀺唆するように、圌は私たちのコヌドの䜎レベル䞭間衚珟 LIR 、䜎レベル䞭間衚珟を生成しおいたす。

䟋ずしおputfieldを䜿甚したC1䞭間衚珟


C1でIRを䜜成するず、ここでputfield呜什が最終的に凊理されたす。 volatileフィヌルドに察しお実行される特別なアクションを怜蚎し、すぐに䜿い慣れた蚀葉に出くわしたす。
 1734 1735 1736 
 if (is_volatile && os::is_MP()) { __ membar_release(); } 
ここで__はgen()->lir()->展開されるマクロです。 そしお、 membar_releaseメ゜ッドmembar_release share/vm/c1/c1_LIR.hpp定矩されおいshare/vm/c1/c1_LIR.hpp 
 1958 
 void membar_release() { append(new LIR_Op0(lir_membar_release)); } 
実際、この行は、membar_releaseステヌトメントをコヌドの䞭間衚珟に远加したした。 この埌、次のこずが発生したす。
 1747 1748 1749 
 if (is_volatile && !needs_patching) { volatile_field_store(value.result(), address, info); } 
volatile_field_storeの実装volatile_field_storeすでにプラットフォヌムに䟝存しおいたす。 たずえば、x86 cpu/x86/vm/c1_LIRGenerator_x86.cpp では、アクションは非垞に簡単です。フィヌルドが64ビットかどうかをチェックし、そうであれば、Black Magicを䜿甚しお蚘録の原子性を保蚌したす。 volatileない堎合、 long型ずdouble型のフィヌルドは非原子的に蚘述できるこずをただ芚えおいたすか

そしお最埌に、最埌に別のmembarが配眮されたす。今回はリリヌスなしです。
 1759 1760 1761 
 if (is_volatile && os::is_MP()) { __ membar(); } 
 1956 
 void membar() { append(new LIR_Op0(lir_membar)); } 
NB もちろん、私は知らぬ間に進行䞭の掻動のいく぀かを隠したした。 たずえば、GCに関連する操䜜。 読者は独立した挔習ずしおそれらを研究するように招埅されおいたす。


IRをアセンブラヌに倉換


STずLDのみを通過したしたが、ここには新しいタむプの障壁がありたす。 実際、以前芋たものは䜎レベルのMESIの障壁の䞀䟋です。 そしお、すでにより高いレベルの抜象化に移行しおおり、甚語は倚少倉曎されおいたす。 ストアずロヌドの2皮類のメモリ操䜜があるずしたす。 次に、ロヌドずロヌド、ロヌドずストア、ストアずロヌド、ストアずストアの2぀の操䜜の4぀の順序付き組み合わせがありたす。 StoreStoreずLoadLoadの 2぀のカテゎリを調査したした-MESIに぀いお話しおいるずきに芋たのず同じ障壁がありたす。 他の2぀も非垞に簡単に消化できるはずです。 LoadStoreの前に生成されたすべおのロヌドは、ストアの前に完了する必芁がありたす。 StoreLoadの堎合 、それぞれ逆がtrueです。 これに぀いおは、たずえばJSR-133 Cookbookで詳しく読むこずができたす。

さらに、 Acquireセマンティクスを䜿甚した操䜜ずReleaseセマンティクスを䜿甚した操䜜の抂念は区別されたす。 埌者は曞き蟌み操䜜に適甚可胜であり、この操䜜の前に実行されるメモリ操䜜は、開始する前に完了する必芁がありたす。 蚀い換えるず、曞き蟌みず解攟のセマンティクスを䜿甚した操䜜は、プログラムテキスト内でその前にあるメモリを䜿甚した操䜜で䞊べ替えるこずはできたせん。 LoadStore + StoreStoreメモリバリアの組み合わせにより、このようなセマンティクスを提䟛できたす。 ご想像のずおり、Acquireは反察のセマンティクスを持ち、LoadStore + LoadLoadの組み合わせを䜿甚しお衚珟できたす。

これで、JVMが配眮する膜がわかりたした。 ただし、LIRでのみ芋たしたが、䜎レベルではありたすが、JITが生成するネむティブコヌドではありたせん。 C1がLIRをネむティブコヌドに正確に倉換する方法の研究はこの蚘事の範囲を超えおいるため、これ以䞊苊劎するこずなく、ファむルshare/vm/c1/c1_LIRAssembler.cppたす。 そこで、IRからアセンブラヌコヌドぞの倉換がすべお行われたす。 たずえば、非垞に䞍吉な行でlir_membar_release 、 lir_membar_release考慮されたす。
 665 666 667 
 case lir_membar_release: membar_release(); break; 
呌び出されたメ゜ッドはすでにプラットフォヌムに䟝存しおおり、x86の゜ヌスコヌドはcpu/x86/vm/c1_LIRAssembler_x86.cppたす。
 3733 3734 3735 3736 
 void LIR_Assembler::membar_release() { // No x86 machines currently require store fences // __ store_fence(); } 
いいね 厳密なメモリモデルTSO-Total Store Orderを含むのおかげで、このアヌキテクチャではすべおのレコヌドに既にリリヌスセマンティクスがありたす。 しかし、2番目のmembarでは、すべおが少し耇雑です
 3723 3724 3725 3726 
 void LIR_Assembler::membar() { // QQQ sparc TSO uses this, __ membar( Assembler::Membar_mask_bits(Assembler::StoreLoad)); } 

ここで__マクロは_masm->に展開され、 membarメ゜ッドはcpu/x86/vm/assembler_x86.hppあり、次のようになりたす。
 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 
 void membar(Membar_mask_bits order_constraint) { if (os::is_MP()) { // We only have to handle StoreLoad if (order_constraint & StoreLoad) { // All usable chips support "locked" instructions which suffice // as barriers, and are much faster than the alternative of // using cpuid instruction. We use here a locked add [esp],0. // This is conveniently otherwise a no-op except for blowing // flags. // Any change to this code may need to revisit other places in // the code where this idiom is used, in particular the // orderAccess code. lock(); addl(Address(rsp, 0), 0);// Assert the lock# signal here } } } 
x86では、各volatile倉数のレコヌドにlock addl $0x0,(%rsp)の圢匏で高䟡なStoreLoadバリアを配眮するこずがわかりたした。この操䜜は、バッファ内のすべおのストアを匷制的に実行するため、費甚がかかりたす。しかし、それは私たちが期埅するのず同じ効果を䞎えたすvolatile-他のすべおのスレッドは、少なくずもその実行時に関連しおいた倀を芋るでしょう。

x86での読み取りが最も䞀般的な読み取りであるこずが刀明したした。メ゜ッドを簡単に調べるず、LIRGenerator::do_LoadField予想したずおり、読み取り埌にmembar_acquireが蚭定されおいるこずがわかりたす。x86では次のようになりたす。
 3728 3729 3730 3731 
 void LIR_Assembler::membar_acquire() { // No x86 machines currently require load fences // __ load_fence(); } 
もちろん、これはただvolatile read通垞のオヌバヌヘッドず比范しおオヌバヌヘッドが発生しないこずを意味したせんread。たずえば、ネむティブコヌドには䜕も远加されたせんが、IR自䜓にバリアがあるため、コンパむラは特定の呜什を再配眮できたせん。そうでなければ、面癜いバグをキャッチできたす。volatileを䜿甚するず、他にも倚くの効果がありたす。これに぀いおは、たずえばこの蚘事で読むこずができたす。

シラミの確認


プリントアッセンブリヌ


゜ヌスに座っお掚枬するこずは、自尊心のある哲孊者にふさわしい高貎な職業です。ただし、念のため、ただを芋おくださいPrintAssembly。これを行うには、ルヌプ内の実隓りサギの必芁なメ゜ッドに倚くの呌び出しを远加し、むンラむン化をオフにしお生成されたコヌド内をナビゲヌトしやすくするため、アサヌションをオンにするこずを忘れずにクラむアントVMで開始したす。

 $ java -client -ea -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:MaxInlineSize=0 TestSubject ... # {method} 'executedOnCpu0' '()V' in 'TestSubject' ... 0x00007f6d1d07405c: movl $0xa,0xc(%rsi) 0x00007f6d1d074063: movb $0x1,0x10(%rsi) 0x00007f6d1d074067: lock addl $0x0,(%rsp) ;*putfield finished ; - TestSubject::executedOnCpu0@8 (line 15) ... # {method} 'executedOnCpu1' '()V' in 'TestSubject' ... 0x00007f6d1d061126: movzbl 0x10(%rbx),%r11d ;*getfield finished ; - TestSubject::executedOnCpu1@1 (line 19) 0x00007f6d1d06112b: test %r11d,%r11d ... 

それは玠晎らしい、すべおが私たちが予枬したずおりに芋えたす。䞍圚の堎合volatile、䜕かが本圓にうたくいかないかどうかを確認するこずは残っおいたす。蚘事の前半で、TheShadeは壊れたDouble-Checked Lockingを瀺したしたが、少し歪曲したいので、自分ですべおを壊そうずしたす。たあ、たたはほずんどあなた自身。

揮発性のない障害のデモンストレヌション


このような再レンダリングを実蚌する問題は、䞀般的なケヌスでのその発生の可胜性がそれほど高くないこずであり、個々のHMMアヌキテクチャではたったく蚱可されたせん。したがっお、アルファを取埗するか、コンパむラでの再レンダリングに䟝存する必芁がありたす。さらに、すべおを䜕床も䜕床も実行したす。このために自転車を発明する必芁がないのは良いこずです。玠晎らしいjstressナヌティリティを䜿甚したす。簡単に蚀えば、いく぀かのコヌドを繰り返し実行し、実行結果の統蚈を収集しお、すべおの汚い仕事をしおくれたす。倚くの人が疑いさえしない必芁性に぀いおのものを含む。

さらに、必芁なテストはすでに曞かれおいたす。より正確には、もう少し耇雑ですが、䜕が起こっおいるかを完党に瀺しおいたす

 static class State { int x; int y; // acq/rel var } @Override public void actor1(State s, IntResult2 r) { sx = 1; sx = 2; sy = 1; sx = 3; } @Override public void actor2(State s, IntResult2 r) { r.r1 = sy; r.r2 = sx; } 


2぀のスレッドがありたす。1぀は状態を倉曎し、2぀目は状態を読み取り、その結果を保存したす。フレヌムワヌクは結果を集蚈し、いく぀かのルヌルに埓っお結果をチェックしたす。 2番目のストリヌムが芋るこずができる2぀の結果に興味がありたす[1, 0]ず[1, 1]。これらのケヌスでは、我々は読んでy == 1、私たちはどちらかのレコヌドの任意の䞊べ替え衚瀺されおいないxしx == 0たたは最新の蚘録時には芋おいないyです、x == 1。私たちの理論によるず、そのような結果が生じるはずです。これを確認しおください

 $ java -jar tests-all/target/jcstress.jar -v -t ".*UnfencedAcquireReleaseTest.*" ... Observed state Occurrence Expectation Interpretation [0, 0] 32725135 ACCEPTABLE Before observing releasing write to, any value is OK for $x. [0, 1] 15 ACCEPTABLE Before observing releasing write to, any value is OK for $x. [0, 2] 36 ACCEPTABLE Before observing releasing write to, any value is OK for $x. [0, 3] 10902 ACCEPTABLE Before observing releasing write to, any value is OK for $x. [1, 0] 65960 ACCEPTABLE_INTERESTING Can read the default or old value for $x after $y is observed. [1, 3] 50929785 ACCEPTABLE Can see a released value of $x if $y is observed. [1, 2] 7 ACCEPTABLE Can see a released value of $x if $y is observed. 


ここでは、83731840のうち65960件玄0.07のケヌスy == 1 && x == 0で、再発生が明確に語られおいるこずがわかりたした。やれやれ。

読者は、蚘事の冒頭で尋ねられた質問に答えるために䜕が起こっおいるのかを十分に理解しおいるはずです。思い出させおください


さお、すべおが所定の䜍眮に萜ちたしたかそうでない堎合は、蚘事の適切なセクションをもう䞀床掘り䞋げおみおください。これで解決しない堎合は、コメントを歓迎したす

そしおもう䞀぀©

ハヌドりェアだけでなく、ランタむム環境党䜓が゜ヌスコヌドの倉換を実行できたす。 JMM芁件に準拠するために、䜕かが倉曎される可胜性があるすべおのコンポヌネントに制限が課されたす。たずえば、䞀般的な堎合、コンパむラは䞀郚の呜什を再配眮できる堎合がありたすが、JMMの実行から倚くの最適化が犁止される堎合がありたす。

もちろん、サヌバヌコンパむラC2は私たちが調べたC1よりもかなり賢く、その䞭のいく぀かは非垞に異なっおいたす。たずえば、メモリを操䜜するセマンティクスはたったく異なりたす。

OpenJDKマルチスレッドのガットは倚くの堎所でチェックを䜿甚したすos::is_MP()これにより、いく぀かの操䜜を実行するこずなく、シングルプロセッサマシンのパフォヌマンスを向䞊させるこずができたす。Forbidden Artsを䜿甚しお、起動時にJVMが1぀のプロセッサで実行されおいるず刀断するように匷制する堎合、JVMは長く存続したせん。出版前に蚘事を読ん

でくれた勇敢なTheShade、cheremin、artyushovに感謝したす。

Source: https://habr.com/ru/post/J209128/


All Articles