what is the difference between ++, add operation and fetch_add() in atomic()

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
19
down vote

favorite
2












I ran following code many times but why the result for prefix increment , fetch_add() shows the correct result while with add operation (+), it prints the wrong result?



#include <iostream>
#include <mutex>
#include <future>
using namespace std;
atomic <int> cnt (0);
void fun()

for(int i =0; i <10000000 ; ++i)

//++cnt; // print the correct result 20000000
//cnt = cnt+1; // print wrong result, arbitrary numbers
cnt.fetch_add(1); // print the correct result 20000000


int main()

auto fut1 = async(std::launch::async, fun);
auto fut2 = async(std::launch::async, fun);
fut1.get();
fut2.get();
cout << "value of cnt: "<<cnt <<endl;








share|improve this question
























    up vote
    19
    down vote

    favorite
    2












    I ran following code many times but why the result for prefix increment , fetch_add() shows the correct result while with add operation (+), it prints the wrong result?



    #include <iostream>
    #include <mutex>
    #include <future>
    using namespace std;
    atomic <int> cnt (0);
    void fun()

    for(int i =0; i <10000000 ; ++i)

    //++cnt; // print the correct result 20000000
    //cnt = cnt+1; // print wrong result, arbitrary numbers
    cnt.fetch_add(1); // print the correct result 20000000


    int main()

    auto fut1 = async(std::launch::async, fun);
    auto fut2 = async(std::launch::async, fun);
    fut1.get();
    fut2.get();
    cout << "value of cnt: "<<cnt <<endl;








    share|improve this question






















      up vote
      19
      down vote

      favorite
      2









      up vote
      19
      down vote

      favorite
      2






      2





      I ran following code many times but why the result for prefix increment , fetch_add() shows the correct result while with add operation (+), it prints the wrong result?



      #include <iostream>
      #include <mutex>
      #include <future>
      using namespace std;
      atomic <int> cnt (0);
      void fun()

      for(int i =0; i <10000000 ; ++i)

      //++cnt; // print the correct result 20000000
      //cnt = cnt+1; // print wrong result, arbitrary numbers
      cnt.fetch_add(1); // print the correct result 20000000


      int main()

      auto fut1 = async(std::launch::async, fun);
      auto fut2 = async(std::launch::async, fun);
      fut1.get();
      fut2.get();
      cout << "value of cnt: "<<cnt <<endl;








      share|improve this question












      I ran following code many times but why the result for prefix increment , fetch_add() shows the correct result while with add operation (+), it prints the wrong result?



      #include <iostream>
      #include <mutex>
      #include <future>
      using namespace std;
      atomic <int> cnt (0);
      void fun()

      for(int i =0; i <10000000 ; ++i)

      //++cnt; // print the correct result 20000000
      //cnt = cnt+1; // print wrong result, arbitrary numbers
      cnt.fetch_add(1); // print the correct result 20000000


      int main()

      auto fut1 = async(std::launch::async, fun);
      auto fut2 = async(std::launch::async, fun);
      fut1.get();
      fut2.get();
      cout << "value of cnt: "<<cnt <<endl;










      share|improve this question











      share|improve this question




      share|improve this question










      asked Aug 29 at 5:04









      Alok

      7671720




      7671720






















          3 Answers
          3






          active

          oldest

          votes

















          up vote
          29
          down vote



          accepted










          ++cnt and cnt.fetch_add(1) are truly atomic operations. One thread is blocked while the other thread reads, increments, and updates the value. As such, the two threads cannot step on each other's toes. Access to cnt is fully serialized, and the final result is as you would expect.



          cnt = cnt+1; is not fully atomic. It involves three separate operations, only two of which are atomic, but one is not. By the time a thread has atomically read the current value of cnt and made a copy of it locally, the other thread is no longer blocked and can freely modify cnt at will while that copy is being incremented. Then, the assignment of the incremented copy back to cnt is done atomically, but will be assigning a stale value if cnt has already been modified by the other thread. So the final result is random and not what you would expect.






          share|improve this answer


















          • 1




            According to the standard cnt.fetch_add(1) is equivalent to cnt++.
            – CAF
            Aug 29 at 6:12






          • 1




            @CAF doesn't matter in this case. cnt.fetch_add(1) is still a single atomic operation, as is cnt++. The code is not acting on the return value.
            – Remy Lebeau
            Aug 29 at 7:59


















          up vote
          10
          down vote













          cnt = cnt+1



          This is not an atomic operation. This first loads cnt in one atomic operation, then does the addition and finally stores the result in another atomic operation. However, the value can be changed after loading which can be overwritten by final store which leads to wrong end result.



          The other two are atomic operations and thus avoid such race condition.



          Note that, operator ++, --, +=, -=, &=, |=, ^= are overloaded in std::atomic to provide atomic operations.






          share|improve this answer





























            up vote
            0
            down vote













            operator ++ is not a single operation but 3 operations load add store, and for ex on arm64 single load or store dosn't generate any data fence, data memory barier.
            for ex atomic_add 1 is a bunch of code with aquire/release semantics



            .LBB2_1: 
            ldaxr x8, [x0] //load exclusive register with aquire
            add x8, x8, #1
            stlxr w9, x8, [x0] //store with rlease
            cbnz w9, .LBB2_1 //if another thread changed value, try again


            where operator ++ will cause race condition if simulateusly used by 2 threads



            ldr x8, [x0]
            add x8, x8, #1 // =1
            str x8, [x0]





            share|improve this answer
















            • 3




              Are you talking about operator++ of std::atomic? It is supposed to be atomic. E. g. GCC compiles both ++ and fetch_add to lock add on amd64.
              – joe_chip
              Aug 29 at 10:40











            • Not just supposed to be, guaranteed. It's even "more" atomic than it needs to be since it is defined as equivalent to fetch_add(1)+1. Which is, lacking a memory order specification, sequentially consistent (although relaxed would almost certainly do fine). See en.cppreference.com/w/cpp/atomic/atomic/operator_arith
              – Damon
              Aug 29 at 12:14










            • i missed that dissucion is about atomic<int> and not an just int increment, my fault
              – Artur Bac
              Aug 29 at 15:21










            • and atomic<>::operator ++ gives exactly same code as fetch_add .LBB0_1: ldaxr x0, [x8] add x9, x0, #1 stlxr w10, x9, [x8] cbnz w10, .LBB0_1
              – Artur Bac
              Aug 29 at 15:27











            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: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            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%2f52069803%2fwhat-is-the-difference-between-add-operation-and-fetch-add-in-atomic%23new-answer', 'question_page');

            );

            Post as a guest






























            3 Answers
            3






            active

            oldest

            votes








            3 Answers
            3






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            29
            down vote



            accepted










            ++cnt and cnt.fetch_add(1) are truly atomic operations. One thread is blocked while the other thread reads, increments, and updates the value. As such, the two threads cannot step on each other's toes. Access to cnt is fully serialized, and the final result is as you would expect.



            cnt = cnt+1; is not fully atomic. It involves three separate operations, only two of which are atomic, but one is not. By the time a thread has atomically read the current value of cnt and made a copy of it locally, the other thread is no longer blocked and can freely modify cnt at will while that copy is being incremented. Then, the assignment of the incremented copy back to cnt is done atomically, but will be assigning a stale value if cnt has already been modified by the other thread. So the final result is random and not what you would expect.






            share|improve this answer


















            • 1




              According to the standard cnt.fetch_add(1) is equivalent to cnt++.
              – CAF
              Aug 29 at 6:12






            • 1




              @CAF doesn't matter in this case. cnt.fetch_add(1) is still a single atomic operation, as is cnt++. The code is not acting on the return value.
              – Remy Lebeau
              Aug 29 at 7:59















            up vote
            29
            down vote



            accepted










            ++cnt and cnt.fetch_add(1) are truly atomic operations. One thread is blocked while the other thread reads, increments, and updates the value. As such, the two threads cannot step on each other's toes. Access to cnt is fully serialized, and the final result is as you would expect.



            cnt = cnt+1; is not fully atomic. It involves three separate operations, only two of which are atomic, but one is not. By the time a thread has atomically read the current value of cnt and made a copy of it locally, the other thread is no longer blocked and can freely modify cnt at will while that copy is being incremented. Then, the assignment of the incremented copy back to cnt is done atomically, but will be assigning a stale value if cnt has already been modified by the other thread. So the final result is random and not what you would expect.






            share|improve this answer


















            • 1




              According to the standard cnt.fetch_add(1) is equivalent to cnt++.
              – CAF
              Aug 29 at 6:12






            • 1




              @CAF doesn't matter in this case. cnt.fetch_add(1) is still a single atomic operation, as is cnt++. The code is not acting on the return value.
              – Remy Lebeau
              Aug 29 at 7:59













            up vote
            29
            down vote



            accepted







            up vote
            29
            down vote



            accepted






            ++cnt and cnt.fetch_add(1) are truly atomic operations. One thread is blocked while the other thread reads, increments, and updates the value. As such, the two threads cannot step on each other's toes. Access to cnt is fully serialized, and the final result is as you would expect.



            cnt = cnt+1; is not fully atomic. It involves three separate operations, only two of which are atomic, but one is not. By the time a thread has atomically read the current value of cnt and made a copy of it locally, the other thread is no longer blocked and can freely modify cnt at will while that copy is being incremented. Then, the assignment of the incremented copy back to cnt is done atomically, but will be assigning a stale value if cnt has already been modified by the other thread. So the final result is random and not what you would expect.






            share|improve this answer














            ++cnt and cnt.fetch_add(1) are truly atomic operations. One thread is blocked while the other thread reads, increments, and updates the value. As such, the two threads cannot step on each other's toes. Access to cnt is fully serialized, and the final result is as you would expect.



            cnt = cnt+1; is not fully atomic. It involves three separate operations, only two of which are atomic, but one is not. By the time a thread has atomically read the current value of cnt and made a copy of it locally, the other thread is no longer blocked and can freely modify cnt at will while that copy is being incremented. Then, the assignment of the incremented copy back to cnt is done atomically, but will be assigning a stale value if cnt has already been modified by the other thread. So the final result is random and not what you would expect.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 29 at 5:27

























            answered Aug 29 at 5:10









            Remy Lebeau

            318k17234415




            318k17234415







            • 1




              According to the standard cnt.fetch_add(1) is equivalent to cnt++.
              – CAF
              Aug 29 at 6:12






            • 1




              @CAF doesn't matter in this case. cnt.fetch_add(1) is still a single atomic operation, as is cnt++. The code is not acting on the return value.
              – Remy Lebeau
              Aug 29 at 7:59













            • 1




              According to the standard cnt.fetch_add(1) is equivalent to cnt++.
              – CAF
              Aug 29 at 6:12






            • 1




              @CAF doesn't matter in this case. cnt.fetch_add(1) is still a single atomic operation, as is cnt++. The code is not acting on the return value.
              – Remy Lebeau
              Aug 29 at 7:59








            1




            1




            According to the standard cnt.fetch_add(1) is equivalent to cnt++.
            – CAF
            Aug 29 at 6:12




            According to the standard cnt.fetch_add(1) is equivalent to cnt++.
            – CAF
            Aug 29 at 6:12




            1




            1




            @CAF doesn't matter in this case. cnt.fetch_add(1) is still a single atomic operation, as is cnt++. The code is not acting on the return value.
            – Remy Lebeau
            Aug 29 at 7:59





            @CAF doesn't matter in this case. cnt.fetch_add(1) is still a single atomic operation, as is cnt++. The code is not acting on the return value.
            – Remy Lebeau
            Aug 29 at 7:59













            up vote
            10
            down vote













            cnt = cnt+1



            This is not an atomic operation. This first loads cnt in one atomic operation, then does the addition and finally stores the result in another atomic operation. However, the value can be changed after loading which can be overwritten by final store which leads to wrong end result.



            The other two are atomic operations and thus avoid such race condition.



            Note that, operator ++, --, +=, -=, &=, |=, ^= are overloaded in std::atomic to provide atomic operations.






            share|improve this answer


























              up vote
              10
              down vote













              cnt = cnt+1



              This is not an atomic operation. This first loads cnt in one atomic operation, then does the addition and finally stores the result in another atomic operation. However, the value can be changed after loading which can be overwritten by final store which leads to wrong end result.



              The other two are atomic operations and thus avoid such race condition.



              Note that, operator ++, --, +=, -=, &=, |=, ^= are overloaded in std::atomic to provide atomic operations.






              share|improve this answer
























                up vote
                10
                down vote










                up vote
                10
                down vote









                cnt = cnt+1



                This is not an atomic operation. This first loads cnt in one atomic operation, then does the addition and finally stores the result in another atomic operation. However, the value can be changed after loading which can be overwritten by final store which leads to wrong end result.



                The other two are atomic operations and thus avoid such race condition.



                Note that, operator ++, --, +=, -=, &=, |=, ^= are overloaded in std::atomic to provide atomic operations.






                share|improve this answer














                cnt = cnt+1



                This is not an atomic operation. This first loads cnt in one atomic operation, then does the addition and finally stores the result in another atomic operation. However, the value can be changed after loading which can be overwritten by final store which leads to wrong end result.



                The other two are atomic operations and thus avoid such race condition.



                Note that, operator ++, --, +=, -=, &=, |=, ^= are overloaded in std::atomic to provide atomic operations.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Aug 29 at 5:27

























                answered Aug 29 at 5:11









                taskinoor

                38.1k794122




                38.1k794122




















                    up vote
                    0
                    down vote













                    operator ++ is not a single operation but 3 operations load add store, and for ex on arm64 single load or store dosn't generate any data fence, data memory barier.
                    for ex atomic_add 1 is a bunch of code with aquire/release semantics



                    .LBB2_1: 
                    ldaxr x8, [x0] //load exclusive register with aquire
                    add x8, x8, #1
                    stlxr w9, x8, [x0] //store with rlease
                    cbnz w9, .LBB2_1 //if another thread changed value, try again


                    where operator ++ will cause race condition if simulateusly used by 2 threads



                    ldr x8, [x0]
                    add x8, x8, #1 // =1
                    str x8, [x0]





                    share|improve this answer
















                    • 3




                      Are you talking about operator++ of std::atomic? It is supposed to be atomic. E. g. GCC compiles both ++ and fetch_add to lock add on amd64.
                      – joe_chip
                      Aug 29 at 10:40











                    • Not just supposed to be, guaranteed. It's even "more" atomic than it needs to be since it is defined as equivalent to fetch_add(1)+1. Which is, lacking a memory order specification, sequentially consistent (although relaxed would almost certainly do fine). See en.cppreference.com/w/cpp/atomic/atomic/operator_arith
                      – Damon
                      Aug 29 at 12:14










                    • i missed that dissucion is about atomic<int> and not an just int increment, my fault
                      – Artur Bac
                      Aug 29 at 15:21










                    • and atomic<>::operator ++ gives exactly same code as fetch_add .LBB0_1: ldaxr x0, [x8] add x9, x0, #1 stlxr w10, x9, [x8] cbnz w10, .LBB0_1
                      – Artur Bac
                      Aug 29 at 15:27















                    up vote
                    0
                    down vote













                    operator ++ is not a single operation but 3 operations load add store, and for ex on arm64 single load or store dosn't generate any data fence, data memory barier.
                    for ex atomic_add 1 is a bunch of code with aquire/release semantics



                    .LBB2_1: 
                    ldaxr x8, [x0] //load exclusive register with aquire
                    add x8, x8, #1
                    stlxr w9, x8, [x0] //store with rlease
                    cbnz w9, .LBB2_1 //if another thread changed value, try again


                    where operator ++ will cause race condition if simulateusly used by 2 threads



                    ldr x8, [x0]
                    add x8, x8, #1 // =1
                    str x8, [x0]





                    share|improve this answer
















                    • 3




                      Are you talking about operator++ of std::atomic? It is supposed to be atomic. E. g. GCC compiles both ++ and fetch_add to lock add on amd64.
                      – joe_chip
                      Aug 29 at 10:40











                    • Not just supposed to be, guaranteed. It's even "more" atomic than it needs to be since it is defined as equivalent to fetch_add(1)+1. Which is, lacking a memory order specification, sequentially consistent (although relaxed would almost certainly do fine). See en.cppreference.com/w/cpp/atomic/atomic/operator_arith
                      – Damon
                      Aug 29 at 12:14










                    • i missed that dissucion is about atomic<int> and not an just int increment, my fault
                      – Artur Bac
                      Aug 29 at 15:21










                    • and atomic<>::operator ++ gives exactly same code as fetch_add .LBB0_1: ldaxr x0, [x8] add x9, x0, #1 stlxr w10, x9, [x8] cbnz w10, .LBB0_1
                      – Artur Bac
                      Aug 29 at 15:27













                    up vote
                    0
                    down vote










                    up vote
                    0
                    down vote









                    operator ++ is not a single operation but 3 operations load add store, and for ex on arm64 single load or store dosn't generate any data fence, data memory barier.
                    for ex atomic_add 1 is a bunch of code with aquire/release semantics



                    .LBB2_1: 
                    ldaxr x8, [x0] //load exclusive register with aquire
                    add x8, x8, #1
                    stlxr w9, x8, [x0] //store with rlease
                    cbnz w9, .LBB2_1 //if another thread changed value, try again


                    where operator ++ will cause race condition if simulateusly used by 2 threads



                    ldr x8, [x0]
                    add x8, x8, #1 // =1
                    str x8, [x0]





                    share|improve this answer












                    operator ++ is not a single operation but 3 operations load add store, and for ex on arm64 single load or store dosn't generate any data fence, data memory barier.
                    for ex atomic_add 1 is a bunch of code with aquire/release semantics



                    .LBB2_1: 
                    ldaxr x8, [x0] //load exclusive register with aquire
                    add x8, x8, #1
                    stlxr w9, x8, [x0] //store with rlease
                    cbnz w9, .LBB2_1 //if another thread changed value, try again


                    where operator ++ will cause race condition if simulateusly used by 2 threads



                    ldr x8, [x0]
                    add x8, x8, #1 // =1
                    str x8, [x0]






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Aug 29 at 9:19









                    Artur Bac

                    706




                    706







                    • 3




                      Are you talking about operator++ of std::atomic? It is supposed to be atomic. E. g. GCC compiles both ++ and fetch_add to lock add on amd64.
                      – joe_chip
                      Aug 29 at 10:40











                    • Not just supposed to be, guaranteed. It's even "more" atomic than it needs to be since it is defined as equivalent to fetch_add(1)+1. Which is, lacking a memory order specification, sequentially consistent (although relaxed would almost certainly do fine). See en.cppreference.com/w/cpp/atomic/atomic/operator_arith
                      – Damon
                      Aug 29 at 12:14










                    • i missed that dissucion is about atomic<int> and not an just int increment, my fault
                      – Artur Bac
                      Aug 29 at 15:21










                    • and atomic<>::operator ++ gives exactly same code as fetch_add .LBB0_1: ldaxr x0, [x8] add x9, x0, #1 stlxr w10, x9, [x8] cbnz w10, .LBB0_1
                      – Artur Bac
                      Aug 29 at 15:27













                    • 3




                      Are you talking about operator++ of std::atomic? It is supposed to be atomic. E. g. GCC compiles both ++ and fetch_add to lock add on amd64.
                      – joe_chip
                      Aug 29 at 10:40











                    • Not just supposed to be, guaranteed. It's even "more" atomic than it needs to be since it is defined as equivalent to fetch_add(1)+1. Which is, lacking a memory order specification, sequentially consistent (although relaxed would almost certainly do fine). See en.cppreference.com/w/cpp/atomic/atomic/operator_arith
                      – Damon
                      Aug 29 at 12:14










                    • i missed that dissucion is about atomic<int> and not an just int increment, my fault
                      – Artur Bac
                      Aug 29 at 15:21










                    • and atomic<>::operator ++ gives exactly same code as fetch_add .LBB0_1: ldaxr x0, [x8] add x9, x0, #1 stlxr w10, x9, [x8] cbnz w10, .LBB0_1
                      – Artur Bac
                      Aug 29 at 15:27








                    3




                    3




                    Are you talking about operator++ of std::atomic? It is supposed to be atomic. E. g. GCC compiles both ++ and fetch_add to lock add on amd64.
                    – joe_chip
                    Aug 29 at 10:40





                    Are you talking about operator++ of std::atomic? It is supposed to be atomic. E. g. GCC compiles both ++ and fetch_add to lock add on amd64.
                    – joe_chip
                    Aug 29 at 10:40













                    Not just supposed to be, guaranteed. It's even "more" atomic than it needs to be since it is defined as equivalent to fetch_add(1)+1. Which is, lacking a memory order specification, sequentially consistent (although relaxed would almost certainly do fine). See en.cppreference.com/w/cpp/atomic/atomic/operator_arith
                    – Damon
                    Aug 29 at 12:14




                    Not just supposed to be, guaranteed. It's even "more" atomic than it needs to be since it is defined as equivalent to fetch_add(1)+1. Which is, lacking a memory order specification, sequentially consistent (although relaxed would almost certainly do fine). See en.cppreference.com/w/cpp/atomic/atomic/operator_arith
                    – Damon
                    Aug 29 at 12:14












                    i missed that dissucion is about atomic<int> and not an just int increment, my fault
                    – Artur Bac
                    Aug 29 at 15:21




                    i missed that dissucion is about atomic<int> and not an just int increment, my fault
                    – Artur Bac
                    Aug 29 at 15:21












                    and atomic<>::operator ++ gives exactly same code as fetch_add .LBB0_1: ldaxr x0, [x8] add x9, x0, #1 stlxr w10, x9, [x8] cbnz w10, .LBB0_1
                    – Artur Bac
                    Aug 29 at 15:27





                    and atomic<>::operator ++ gives exactly same code as fetch_add .LBB0_1: ldaxr x0, [x8] add x9, x0, #1 stlxr w10, x9, [x8] cbnz w10, .LBB0_1
                    – Artur Bac
                    Aug 29 at 15:27


















                     

                    draft saved


                    draft discarded















































                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52069803%2fwhat-is-the-difference-between-add-operation-and-fetch-add-in-atomic%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Comments

                    Popular posts from this blog

                    What does second last employer means? [closed]

                    Installing NextGIS Connect into QGIS 3?

                    One-line joke