Setting an AtomicBoolean again











up vote
4
down vote

favorite
2












I am using an AtomicBoolean to enforce volatile visibility between threads. One thread is updating the value, another thread is only reading it.



Say the current value is true. Now say a write thread sets its value to true again:



final AtomicBoolean b = new AtomicBoolean(); // shared between threads

b.set(true);
// ... some time later
b.set(true);


After this 'dummy' set(true), is there a performance penalty when the read thread calls get()? Does the read thread have to re-read and cache the value?



If that is the case, the write thread could have done:



b.compareAndSet(false, true);


This way, the read thread only has to invalidate for real changes.










share|improve this question




























    up vote
    4
    down vote

    favorite
    2












    I am using an AtomicBoolean to enforce volatile visibility between threads. One thread is updating the value, another thread is only reading it.



    Say the current value is true. Now say a write thread sets its value to true again:



    final AtomicBoolean b = new AtomicBoolean(); // shared between threads

    b.set(true);
    // ... some time later
    b.set(true);


    After this 'dummy' set(true), is there a performance penalty when the read thread calls get()? Does the read thread have to re-read and cache the value?



    If that is the case, the write thread could have done:



    b.compareAndSet(false, true);


    This way, the read thread only has to invalidate for real changes.










    share|improve this question


























      up vote
      4
      down vote

      favorite
      2









      up vote
      4
      down vote

      favorite
      2






      2





      I am using an AtomicBoolean to enforce volatile visibility between threads. One thread is updating the value, another thread is only reading it.



      Say the current value is true. Now say a write thread sets its value to true again:



      final AtomicBoolean b = new AtomicBoolean(); // shared between threads

      b.set(true);
      // ... some time later
      b.set(true);


      After this 'dummy' set(true), is there a performance penalty when the read thread calls get()? Does the read thread have to re-read and cache the value?



      If that is the case, the write thread could have done:



      b.compareAndSet(false, true);


      This way, the read thread only has to invalidate for real changes.










      share|improve this question















      I am using an AtomicBoolean to enforce volatile visibility between threads. One thread is updating the value, another thread is only reading it.



      Say the current value is true. Now say a write thread sets its value to true again:



      final AtomicBoolean b = new AtomicBoolean(); // shared between threads

      b.set(true);
      // ... some time later
      b.set(true);


      After this 'dummy' set(true), is there a performance penalty when the read thread calls get()? Does the read thread have to re-read and cache the value?



      If that is the case, the write thread could have done:



      b.compareAndSet(false, true);


      This way, the read thread only has to invalidate for real changes.







      java multithreading caching concurrency volatile






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 22 at 17:07

























      asked Nov 22 at 16:26









      OhNoOhYes

      233




      233
























          2 Answers
          2






          active

          oldest

          votes

















          up vote
          3
          down vote



          accepted










          compareAndSet():



          public final boolean compareAndSet(boolean expect, boolean update) {
          int e = expect ? 1 : 0;
          int u = update ? 1 : 0;
          return unsafe.compareAndSwapInt(this, valueOffset, e, u);
          }


          compareAndSwapInt() is native already:



          UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
          UnsafeWrapper("Unsafe_CompareAndSwapInt");
          oop p = JNIHandles::resolve(obj);
          jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
          return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
          UNSAFE_END


          Where Atomic::cmpxchg is generated somewhere at the beginning of JVM execution as



            address generate_atomic_cmpxchg() {
          StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg");
          address start = __ pc();

          __ movl(rax, c_rarg2);
          if ( os::is_MP() ) __ lock();
          __ cmpxchgl(c_rarg0, Address(c_rarg1, 0));
          __ ret(0);

          return start;
          }


          cmpxchgl() generates x86 code (it has a longer, legacy code path too, so I do not copy that one here):



           InstructionMark im(this);
          prefix(adr, reg);
          emit_byte(0x0F);
          emit_byte(0xB1);
          emit_operand(reg, adr);


          0F B1 is really a CMPXCHG operation. If you check the code above, if ( os::is_MP() ) __ lock(); emits a LOCK prefix on multiprocessor machines (let me just skip quoting lock(), it emits a single F0 byte), so practically everywhere.



          And as the CMPXCHG docs says:




          This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically. To simplify the interface to the processor’s bus, the destination operand receives a write cycle without regard to the result of the comparison. The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination. (The processor never produces a locked read without also producing a locked write.)




          So on a multiprocessor x86 machine, the NOP-CAS also does a write, affecting the cache-line. (Emphasis was added by me)






          share|improve this answer























          • Thank you for the reference. +1
            – Peter Lawrey
            Nov 22 at 17:26










          • Thank you, very detailed.
            – OhNoOhYes
            Nov 22 at 17:45










          • TL;DR conclusion: b.set(true) or b.compareAndSet(false, true) does not make any difference at all for the reading thread. In both cases, it has to reload the value.
            – OhNoOhYes
            Nov 22 at 17:46


















          up vote
          1
          down vote













          Both a write and a CAS "touch" the cache line triggering the cache line to be dirty.



          However the cost is relatively small, in the order of 30 - 50 ns.



          The cost of the code not being warmed up because it hasn't been run 10,000 times yet, is likely to be far greater.






          share|improve this answer





















          • Why does a no-op CAS also touch the cache line? For example compareAndSet(true, true).
            – OhNoOhYes
            Nov 22 at 16:37












          • @OhNoOh It could but on Intel x64, it doesn't appear to. For example, we use compareAndSet(0L, 0L) on off-heap memory to pull in a new page without altering it. i.e. pre-touch it to reduce the impact of creating a page on demand.
            – Peter Lawrey
            Nov 22 at 16:59











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53434995%2fsetting-an-atomicboolean-again%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          3
          down vote



          accepted










          compareAndSet():



          public final boolean compareAndSet(boolean expect, boolean update) {
          int e = expect ? 1 : 0;
          int u = update ? 1 : 0;
          return unsafe.compareAndSwapInt(this, valueOffset, e, u);
          }


          compareAndSwapInt() is native already:



          UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
          UnsafeWrapper("Unsafe_CompareAndSwapInt");
          oop p = JNIHandles::resolve(obj);
          jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
          return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
          UNSAFE_END


          Where Atomic::cmpxchg is generated somewhere at the beginning of JVM execution as



            address generate_atomic_cmpxchg() {
          StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg");
          address start = __ pc();

          __ movl(rax, c_rarg2);
          if ( os::is_MP() ) __ lock();
          __ cmpxchgl(c_rarg0, Address(c_rarg1, 0));
          __ ret(0);

          return start;
          }


          cmpxchgl() generates x86 code (it has a longer, legacy code path too, so I do not copy that one here):



           InstructionMark im(this);
          prefix(adr, reg);
          emit_byte(0x0F);
          emit_byte(0xB1);
          emit_operand(reg, adr);


          0F B1 is really a CMPXCHG operation. If you check the code above, if ( os::is_MP() ) __ lock(); emits a LOCK prefix on multiprocessor machines (let me just skip quoting lock(), it emits a single F0 byte), so practically everywhere.



          And as the CMPXCHG docs says:




          This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically. To simplify the interface to the processor’s bus, the destination operand receives a write cycle without regard to the result of the comparison. The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination. (The processor never produces a locked read without also producing a locked write.)




          So on a multiprocessor x86 machine, the NOP-CAS also does a write, affecting the cache-line. (Emphasis was added by me)






          share|improve this answer























          • Thank you for the reference. +1
            – Peter Lawrey
            Nov 22 at 17:26










          • Thank you, very detailed.
            – OhNoOhYes
            Nov 22 at 17:45










          • TL;DR conclusion: b.set(true) or b.compareAndSet(false, true) does not make any difference at all for the reading thread. In both cases, it has to reload the value.
            – OhNoOhYes
            Nov 22 at 17:46















          up vote
          3
          down vote



          accepted










          compareAndSet():



          public final boolean compareAndSet(boolean expect, boolean update) {
          int e = expect ? 1 : 0;
          int u = update ? 1 : 0;
          return unsafe.compareAndSwapInt(this, valueOffset, e, u);
          }


          compareAndSwapInt() is native already:



          UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
          UnsafeWrapper("Unsafe_CompareAndSwapInt");
          oop p = JNIHandles::resolve(obj);
          jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
          return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
          UNSAFE_END


          Where Atomic::cmpxchg is generated somewhere at the beginning of JVM execution as



            address generate_atomic_cmpxchg() {
          StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg");
          address start = __ pc();

          __ movl(rax, c_rarg2);
          if ( os::is_MP() ) __ lock();
          __ cmpxchgl(c_rarg0, Address(c_rarg1, 0));
          __ ret(0);

          return start;
          }


          cmpxchgl() generates x86 code (it has a longer, legacy code path too, so I do not copy that one here):



           InstructionMark im(this);
          prefix(adr, reg);
          emit_byte(0x0F);
          emit_byte(0xB1);
          emit_operand(reg, adr);


          0F B1 is really a CMPXCHG operation. If you check the code above, if ( os::is_MP() ) __ lock(); emits a LOCK prefix on multiprocessor machines (let me just skip quoting lock(), it emits a single F0 byte), so practically everywhere.



          And as the CMPXCHG docs says:




          This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically. To simplify the interface to the processor’s bus, the destination operand receives a write cycle without regard to the result of the comparison. The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination. (The processor never produces a locked read without also producing a locked write.)




          So on a multiprocessor x86 machine, the NOP-CAS also does a write, affecting the cache-line. (Emphasis was added by me)






          share|improve this answer























          • Thank you for the reference. +1
            – Peter Lawrey
            Nov 22 at 17:26










          • Thank you, very detailed.
            – OhNoOhYes
            Nov 22 at 17:45










          • TL;DR conclusion: b.set(true) or b.compareAndSet(false, true) does not make any difference at all for the reading thread. In both cases, it has to reload the value.
            – OhNoOhYes
            Nov 22 at 17:46













          up vote
          3
          down vote



          accepted







          up vote
          3
          down vote



          accepted






          compareAndSet():



          public final boolean compareAndSet(boolean expect, boolean update) {
          int e = expect ? 1 : 0;
          int u = update ? 1 : 0;
          return unsafe.compareAndSwapInt(this, valueOffset, e, u);
          }


          compareAndSwapInt() is native already:



          UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
          UnsafeWrapper("Unsafe_CompareAndSwapInt");
          oop p = JNIHandles::resolve(obj);
          jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
          return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
          UNSAFE_END


          Where Atomic::cmpxchg is generated somewhere at the beginning of JVM execution as



            address generate_atomic_cmpxchg() {
          StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg");
          address start = __ pc();

          __ movl(rax, c_rarg2);
          if ( os::is_MP() ) __ lock();
          __ cmpxchgl(c_rarg0, Address(c_rarg1, 0));
          __ ret(0);

          return start;
          }


          cmpxchgl() generates x86 code (it has a longer, legacy code path too, so I do not copy that one here):



           InstructionMark im(this);
          prefix(adr, reg);
          emit_byte(0x0F);
          emit_byte(0xB1);
          emit_operand(reg, adr);


          0F B1 is really a CMPXCHG operation. If you check the code above, if ( os::is_MP() ) __ lock(); emits a LOCK prefix on multiprocessor machines (let me just skip quoting lock(), it emits a single F0 byte), so practically everywhere.



          And as the CMPXCHG docs says:




          This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically. To simplify the interface to the processor’s bus, the destination operand receives a write cycle without regard to the result of the comparison. The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination. (The processor never produces a locked read without also producing a locked write.)




          So on a multiprocessor x86 machine, the NOP-CAS also does a write, affecting the cache-line. (Emphasis was added by me)






          share|improve this answer














          compareAndSet():



          public final boolean compareAndSet(boolean expect, boolean update) {
          int e = expect ? 1 : 0;
          int u = update ? 1 : 0;
          return unsafe.compareAndSwapInt(this, valueOffset, e, u);
          }


          compareAndSwapInt() is native already:



          UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
          UnsafeWrapper("Unsafe_CompareAndSwapInt");
          oop p = JNIHandles::resolve(obj);
          jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
          return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
          UNSAFE_END


          Where Atomic::cmpxchg is generated somewhere at the beginning of JVM execution as



            address generate_atomic_cmpxchg() {
          StubCodeMark mark(this, "StubRoutines", "atomic_cmpxchg");
          address start = __ pc();

          __ movl(rax, c_rarg2);
          if ( os::is_MP() ) __ lock();
          __ cmpxchgl(c_rarg0, Address(c_rarg1, 0));
          __ ret(0);

          return start;
          }


          cmpxchgl() generates x86 code (it has a longer, legacy code path too, so I do not copy that one here):



           InstructionMark im(this);
          prefix(adr, reg);
          emit_byte(0x0F);
          emit_byte(0xB1);
          emit_operand(reg, adr);


          0F B1 is really a CMPXCHG operation. If you check the code above, if ( os::is_MP() ) __ lock(); emits a LOCK prefix on multiprocessor machines (let me just skip quoting lock(), it emits a single F0 byte), so practically everywhere.



          And as the CMPXCHG docs says:




          This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically. To simplify the interface to the processor’s bus, the destination operand receives a write cycle without regard to the result of the comparison. The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination. (The processor never produces a locked read without also producing a locked write.)




          So on a multiprocessor x86 machine, the NOP-CAS also does a write, affecting the cache-line. (Emphasis was added by me)







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 22 at 17:33

























          answered Nov 22 at 17:14









          tevemadar

          4,2532723




          4,2532723












          • Thank you for the reference. +1
            – Peter Lawrey
            Nov 22 at 17:26










          • Thank you, very detailed.
            – OhNoOhYes
            Nov 22 at 17:45










          • TL;DR conclusion: b.set(true) or b.compareAndSet(false, true) does not make any difference at all for the reading thread. In both cases, it has to reload the value.
            – OhNoOhYes
            Nov 22 at 17:46


















          • Thank you for the reference. +1
            – Peter Lawrey
            Nov 22 at 17:26










          • Thank you, very detailed.
            – OhNoOhYes
            Nov 22 at 17:45










          • TL;DR conclusion: b.set(true) or b.compareAndSet(false, true) does not make any difference at all for the reading thread. In both cases, it has to reload the value.
            – OhNoOhYes
            Nov 22 at 17:46
















          Thank you for the reference. +1
          – Peter Lawrey
          Nov 22 at 17:26




          Thank you for the reference. +1
          – Peter Lawrey
          Nov 22 at 17:26












          Thank you, very detailed.
          – OhNoOhYes
          Nov 22 at 17:45




          Thank you, very detailed.
          – OhNoOhYes
          Nov 22 at 17:45












          TL;DR conclusion: b.set(true) or b.compareAndSet(false, true) does not make any difference at all for the reading thread. In both cases, it has to reload the value.
          – OhNoOhYes
          Nov 22 at 17:46




          TL;DR conclusion: b.set(true) or b.compareAndSet(false, true) does not make any difference at all for the reading thread. In both cases, it has to reload the value.
          – OhNoOhYes
          Nov 22 at 17:46












          up vote
          1
          down vote













          Both a write and a CAS "touch" the cache line triggering the cache line to be dirty.



          However the cost is relatively small, in the order of 30 - 50 ns.



          The cost of the code not being warmed up because it hasn't been run 10,000 times yet, is likely to be far greater.






          share|improve this answer





















          • Why does a no-op CAS also touch the cache line? For example compareAndSet(true, true).
            – OhNoOhYes
            Nov 22 at 16:37












          • @OhNoOh It could but on Intel x64, it doesn't appear to. For example, we use compareAndSet(0L, 0L) on off-heap memory to pull in a new page without altering it. i.e. pre-touch it to reduce the impact of creating a page on demand.
            – Peter Lawrey
            Nov 22 at 16:59















          up vote
          1
          down vote













          Both a write and a CAS "touch" the cache line triggering the cache line to be dirty.



          However the cost is relatively small, in the order of 30 - 50 ns.



          The cost of the code not being warmed up because it hasn't been run 10,000 times yet, is likely to be far greater.






          share|improve this answer





















          • Why does a no-op CAS also touch the cache line? For example compareAndSet(true, true).
            – OhNoOhYes
            Nov 22 at 16:37












          • @OhNoOh It could but on Intel x64, it doesn't appear to. For example, we use compareAndSet(0L, 0L) on off-heap memory to pull in a new page without altering it. i.e. pre-touch it to reduce the impact of creating a page on demand.
            – Peter Lawrey
            Nov 22 at 16:59













          up vote
          1
          down vote










          up vote
          1
          down vote









          Both a write and a CAS "touch" the cache line triggering the cache line to be dirty.



          However the cost is relatively small, in the order of 30 - 50 ns.



          The cost of the code not being warmed up because it hasn't been run 10,000 times yet, is likely to be far greater.






          share|improve this answer












          Both a write and a CAS "touch" the cache line triggering the cache line to be dirty.



          However the cost is relatively small, in the order of 30 - 50 ns.



          The cost of the code not being warmed up because it hasn't been run 10,000 times yet, is likely to be far greater.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 22 at 16:33









          Peter Lawrey

          439k55556958




          439k55556958












          • Why does a no-op CAS also touch the cache line? For example compareAndSet(true, true).
            – OhNoOhYes
            Nov 22 at 16:37












          • @OhNoOh It could but on Intel x64, it doesn't appear to. For example, we use compareAndSet(0L, 0L) on off-heap memory to pull in a new page without altering it. i.e. pre-touch it to reduce the impact of creating a page on demand.
            – Peter Lawrey
            Nov 22 at 16:59


















          • Why does a no-op CAS also touch the cache line? For example compareAndSet(true, true).
            – OhNoOhYes
            Nov 22 at 16:37












          • @OhNoOh It could but on Intel x64, it doesn't appear to. For example, we use compareAndSet(0L, 0L) on off-heap memory to pull in a new page without altering it. i.e. pre-touch it to reduce the impact of creating a page on demand.
            – Peter Lawrey
            Nov 22 at 16:59
















          Why does a no-op CAS also touch the cache line? For example compareAndSet(true, true).
          – OhNoOhYes
          Nov 22 at 16:37






          Why does a no-op CAS also touch the cache line? For example compareAndSet(true, true).
          – OhNoOhYes
          Nov 22 at 16:37














          @OhNoOh It could but on Intel x64, it doesn't appear to. For example, we use compareAndSet(0L, 0L) on off-heap memory to pull in a new page without altering it. i.e. pre-touch it to reduce the impact of creating a page on demand.
          – Peter Lawrey
          Nov 22 at 16:59




          @OhNoOh It could but on Intel x64, it doesn't appear to. For example, we use compareAndSet(0L, 0L) on off-heap memory to pull in a new page without altering it. i.e. pre-touch it to reduce the impact of creating a page on demand.
          – Peter Lawrey
          Nov 22 at 16:59


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53434995%2fsetting-an-atomicboolean-again%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          What visual should I use to simply compare current year value vs last year in Power BI desktop

          Alexandru Averescu

          Trompette piccolo