Re: About the volatile and MESI Protocal
Nathan Reynolds via Concurrency-interest <[email protected]> Thu, 17 Feb 2022 09:33:33 -0700
| Newsgroups | gmane.comp.java.jsr.166-concurrency |
|---|---|
| Message-ID | <CALMUwcppk5hzmENLRWqzOtHZtxykB19iFUWXkAbGNJt=01kL7A@mail.gmail.com> |
--===============5614169972658420016== Content-Type: multipart/alternative; boundary="0000000000006e472c05d839540f" --0000000000006e472c05d839540f Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Without volatile or fences, JIT is free to hoist the "if (bChanged =3D=3D !bChanged)" out of the for loop. If for some reason JIT decides to not hoist, JIT could also load bChanged into a single register each iteration and then execute "if (bChanged =3D=3D !bChanged)" using the value in the si= ngle register. On the other hand, if JIT is smart enough, it can get rid of the if statement and block altogether since it can never be true according to the Java Memory Model. Since this is very unlike a real code scenario, I doubt JIT has been enhanced for this last case. Without Thread.sleep(1), the loop will execute 10,000 iterations very quickly. JIT can then do its work and have plenty of wiggle room before the Thread.sleep(1000) finishes. Hence, JIT probably hoisted the if statement and block out of the for loop. With Thread.sleep(1), it will take at least 10 seconds for the loop to iterate 10,000 times. The Thread.sleep(1000) finishes much sooner than that. So, the second thread executes bChanged =3D !bChanged rapidly. Sinc= e this loop is interpreted for the first 10,000 iterations, the interpreter is going to load and store the value to/from cache. On x86, this will cause cache line invalidation so that other threads/cores will see the change. The first thread is still executing an interpreted for loop. The interpreter will load from cache bChanged onto the stack twice. On x86 with cache line invalidation, the first thread could load two different values onto the stack. Hence, you see the first thread print. On a different processor with more relaxed memory constraints, the first thread may not ever see bChanged change since the interpreter may not emit the appropriate cache invalidating instructions. If you let the program run long enough for JIT to kick in, it still may not hoist the if statement because the branch is taken. Taking the branch might convince JIT to leave the if statement in place. I don't know JIT well enough to know for sure. If JIT leaves the if statement in the for loop, JIT might load bChanged a single time into a single register each loop. However, these hypotheses would have to be validated with testing and assembly output. If you changed Thread.sleep(1000) to Thread.sleep(20 * 1000), then JIT has a chance to optimize the for loop and hoist the if statement. I would suspect you would never see a print statement. On Thu, Feb 17, 2022 at 4:45 AM Alex Otenko via Concurrency-interest < [email protected]> wrote: > It's not clear where you reckon MESI is at play here. Rather, MESI acts i= n > such a way that you don't notice functional difference with direct access > to memory. > > Engineers forget that volatile is not only about memory barriers, it is > also about compiler barriers. In this particular example the changes done > by the second thread are not necessarily visible to the first thread, sle= ep > or no sleep. This has nothing to do with MESI cache coherence prorocol. > > Alex > > On Thu, 17 Feb 2022, 11:31 =E9=A9=AC=E9=87=8C=E5=A5=A5 via Concurrency-in= terest, < > [email protected]> wrote: > >> hello concurrency-interest: >> >> My quest about the volatile,According to the MESI Protocol "bChan= ged" in second thread modified in STOREBUFFER,then the first thread can not= get the newest >> >> "bChanged",why add the sleep method ,this thread can get the newe= st bChanged? >> >> >> public class NoVolatile { >> //no volatile >> private static boolean bChanged; >> >> public static void main(String[] args) throws InterruptedException { >> new Thread(() -> { >> for (; ; ) { >> if (bChanged =3D=3D !bChanged) { >> System.out.println("!=3D"); >> break; >> } >> =09 >> //why add the sleep method ,this thread can get the newest bChanged? >> // try { >> // Thread.sleep(1); >> // } catch (InterruptedException e) { >> // e.printStackTrace(); >> // } >> } >> System.exit(0); >> }).start(); >> >> Thread.sleep(1000); >> =09 >> new Thread(() -> { >> for (; ; ) { >> bChanged =3D !bChanged; >> } >> }).start(); >> } >> } >> >> _______________________________________________ >> Concurrency-interest mailing list >> [email protected] >> http://cs.oswego.edu/mailman/listinfo/concurrency-interest >> > _______________________________________________ > Concurrency-interest mailing list > [email protected] > http://cs.oswego.edu/mailman/listinfo/concurrency-interest > --0000000000006e472c05d839540f Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr"><div>Without volatile or fences, JIT is free to hoist the = "if (bChanged =3D=3D !bChanged)" out of the for loop.=C2=A0 If fo= r some reason JIT decides to not hoist, JIT could also load bChanged into a= single register each iteration and then execute "if (bChanged =3D=3D !bChanged)" using the value in the single register.= =C2=A0 On the other hand, if JIT is smart enough, it can get rid of the if = statement and block altogether since it can never be true according to the = Java Memory Model.=C2=A0 Since this is very unlike a real code scenario, I = doubt JIT has been enhanced for this last case.</div><div><br></div><div>Wi= thout Thread.sleep(1), the loop will execute 10,000 iterations very quickly= .=C2=A0 JIT can then do its work and have plenty of wiggle room before the = Thread.sleep(1000) finishes.=C2=A0 Hence, JIT probably hoisted the if state= ment and block out of the for loop.</div><div><br></div><div>With Thread.sl= eep(1), it will take at least 10 seconds for the loop to iterate 10,000 tim= es.=C2=A0 The Thread.sleep(1000) finishes much sooner than that.=C2=A0 So, = the second thread executes bChanged =3D !bChanged rapidly.=C2=A0 Since this= loop is interpreted for the first 10,000 iterations, the interpreter is go= ing to load and store the value to/from cache.=C2=A0 On x86, this will caus= e cache line invalidation so that other threads/cores will see the change.= =C2=A0 The first thread is still executing an interpreted for loop.=C2=A0 T= he interpreter will load from cache bChanged onto the stack twice.=C2=A0 On= x86 with cache line invalidation, the first thread could load two differen= t values onto the stack.=C2=A0 Hence, you see the first thread print.=C2=A0= On a different processor with more relaxed memory constraints, the first t= hread may not ever see bChanged change since the interpreter may not emit t= he appropriate cache invalidating instructions.</div><div><br></div><div> If you let the program run long enough for JIT to kick in, it still may not= hoist the if statement because the branch is taken.=C2=A0 Taking the branc= h might convince JIT to leave the if statement in place.=C2=A0 I don't = know JIT well enough to know for sure.=C2=A0 If JIT leaves the if statement= in the for loop, JIT might load bChanged a single time into a single regis= ter each loop.=C2=A0 However, these hypotheses would have to be validated w= ith testing and assembly output. <br></div><div><br></div><div>If you chang= ed Thread.sleep(1000) to Thread.sleep(20 * 1000), then JIT has a chance to = optimize the for loop and hoist the if statement.=C2=A0 I would suspect you= would never see a print statement.</div></div><br><div class=3D"gmail_quot= e"><div dir=3D"ltr" class=3D"gmail_attr">On Thu, Feb 17, 2022 at 4:45 AM Al= ex Otenko via Concurrency-interest <<a href=3D"mailto:concurrency-intere= [email protected]">[email protected]</a>> wrote:<br></di= v><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;borde= r-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir=3D"auto">It= 9;s not clear where you reckon MESI is at play here. Rather, MESI acts in s= uch a way that you don't notice functional difference with direct acces= s to memory.<div dir=3D"auto"><br></div><div dir=3D"auto">Engineers forget = that volatile is not only about memory barriers, it is also about compiler = barriers. In this particular example the changes done by the second thread = are not necessarily visible to the first thread, sleep or no sleep. This ha= s nothing to do with MESI cache coherence prorocol.</div><div dir=3D"auto">= <br></div><div dir=3D"auto">Alex</div></div><br><div class=3D"gmail_quote">= <div dir=3D"ltr" class=3D"gmail_attr">On Thu, 17 Feb 2022, 11:31 =E9=A9=AC= =E9=87=8C=E5=A5=A5 via Concurrency-interest, <<a href=3D"mailto:concurre= [email protected]" target=3D"_blank">[email protected]= o.edu</a>> wrote:<br></div><blockquote class=3D"gmail_quote" style=3D"ma= rgin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:= 1ex"><pre>hello=C2=A0concurrency-interest:</pre><pre> My quest about = the volatile,According to the MESI Protocol<span style=3D"font-family:Verda= na"> "</span>bChanged" in second thread modified in STOREBUFFER,t= hen the first thread can not get the newest</pre><pre> "bChanged= ",why add the sleep method ,this thread can get the newest bChanged?</= pre><pre><br></pre><pre> public class NoVolatile { //no volatile private static boolean bChanged; public static void main(String[] args) throws InterruptedException { new Thread(() -> { for (; ; ) { if (bChanged =3D=3D !bChanged) { System.out.println("!=3D"); break; } =09 //why add the sleep method ,this thread can get the newest bChanged? // try { // Thread.sleep(1); // } catch (InterruptedException e) { // e.printStackTrace(); // } } System.exit(0); }).start(); Thread.sleep(1000); =09 new Thread(() -> { for (; ; ) { bChanged =3D !bChanged; } }).start(); } }<br></pre>_______________________________________________<br> Concurrency-interest mailing list<br> <a href=3D"mailto:[email protected]" rel=3D"noreferrer" ta= rget=3D"_blank">[email protected]</a><br> <a href=3D"http://cs.oswego.edu/mailman/listinfo/concurrency-interest" rel= =3D"noreferrer noreferrer" target=3D"_blank">http://cs.oswego.edu/mailman/l= istinfo/concurrency-interest</a><br> </blockquote></div> _______________________________________________<br> Concurrency-interest mailing list<br> <a href=3D"mailto:[email protected]" target=3D"_blank">Con= [email protected]</a><br> <a href=3D"http://cs.oswego.edu/mailman/listinfo/concurrency-interest" rel= =3D"noreferrer" target=3D"_blank">http://cs.oswego.edu/mailman/listinfo/con= currency-interest</a><br> </blockquote></div> --0000000000006e472c05d839540f-- --===============5614169972658420016== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Concurrency-interest mailing list [email protected] http://cs.oswego.edu/mailman/listinfo/concurrency-interest --===============5614169972658420016==--