what is the difference between ++, add operation and fetch_add() in atomic()
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
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;
c++ c++11
add a comment |
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;
c++ c++11
add a comment |
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;
c++ c++11
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;
c++ c++11
c++ c++11
asked Aug 29 '18 at 5:04
AlokAlok
8201920
8201920
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
++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.
1
According to the standardcnt.fetch_add(1)is equivalent tocnt++.
– CAF
Aug 29 '18 at 6:12
1
@CAF doesn't matter in this case.cnt.fetch_add(1)is still a single atomic operation, as iscnt++. The code is not acting on the return value.
– Remy Lebeau
Aug 29 '18 at 7:59
add a comment |
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.
add a comment |
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]
3
Are you talking aboutoperator++ofstd::atomic? It is supposed to be atomic. E. g. GCC compiles both++andfetch_addtolock addon amd64.
– joe_chip
Aug 29 '18 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 tofetch_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 '18 at 12:14
i missed that dissucion is about atomic<int> and not an just int increment, my fault
– Artur Bac
Aug 29 '18 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 '18 at 15:27
add a comment |
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',
autoActivateHeartbeat: false,
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
++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.
1
According to the standardcnt.fetch_add(1)is equivalent tocnt++.
– CAF
Aug 29 '18 at 6:12
1
@CAF doesn't matter in this case.cnt.fetch_add(1)is still a single atomic operation, as iscnt++. The code is not acting on the return value.
– Remy Lebeau
Aug 29 '18 at 7:59
add a comment |
++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.
1
According to the standardcnt.fetch_add(1)is equivalent tocnt++.
– CAF
Aug 29 '18 at 6:12
1
@CAF doesn't matter in this case.cnt.fetch_add(1)is still a single atomic operation, as iscnt++. The code is not acting on the return value.
– Remy Lebeau
Aug 29 '18 at 7:59
add a comment |
++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.
++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.
edited Aug 29 '18 at 5:27
answered Aug 29 '18 at 5:10
Remy LebeauRemy Lebeau
344k19270463
344k19270463
1
According to the standardcnt.fetch_add(1)is equivalent tocnt++.
– CAF
Aug 29 '18 at 6:12
1
@CAF doesn't matter in this case.cnt.fetch_add(1)is still a single atomic operation, as iscnt++. The code is not acting on the return value.
– Remy Lebeau
Aug 29 '18 at 7:59
add a comment |
1
According to the standardcnt.fetch_add(1)is equivalent tocnt++.
– CAF
Aug 29 '18 at 6:12
1
@CAF doesn't matter in this case.cnt.fetch_add(1)is still a single atomic operation, as iscnt++. The code is not acting on the return value.
– Remy Lebeau
Aug 29 '18 at 7:59
1
1
According to the standard
cnt.fetch_add(1) is equivalent to cnt++.– CAF
Aug 29 '18 at 6:12
According to the standard
cnt.fetch_add(1) is equivalent to cnt++.– CAF
Aug 29 '18 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 '18 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 '18 at 7:59
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
edited Aug 29 '18 at 5:27
answered Aug 29 '18 at 5:11
taskinoortaskinoor
40.2k7102129
40.2k7102129
add a comment |
add a comment |
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]
3
Are you talking aboutoperator++ofstd::atomic? It is supposed to be atomic. E. g. GCC compiles both++andfetch_addtolock addon amd64.
– joe_chip
Aug 29 '18 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 tofetch_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 '18 at 12:14
i missed that dissucion is about atomic<int> and not an just int increment, my fault
– Artur Bac
Aug 29 '18 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 '18 at 15:27
add a comment |
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]
3
Are you talking aboutoperator++ofstd::atomic? It is supposed to be atomic. E. g. GCC compiles both++andfetch_addtolock addon amd64.
– joe_chip
Aug 29 '18 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 tofetch_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 '18 at 12:14
i missed that dissucion is about atomic<int> and not an just int increment, my fault
– Artur Bac
Aug 29 '18 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 '18 at 15:27
add a comment |
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]
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]
answered Aug 29 '18 at 9:19
Artur BacArtur Bac
706
706
3
Are you talking aboutoperator++ofstd::atomic? It is supposed to be atomic. E. g. GCC compiles both++andfetch_addtolock addon amd64.
– joe_chip
Aug 29 '18 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 tofetch_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 '18 at 12:14
i missed that dissucion is about atomic<int> and not an just int increment, my fault
– Artur Bac
Aug 29 '18 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 '18 at 15:27
add a comment |
3
Are you talking aboutoperator++ofstd::atomic? It is supposed to be atomic. E. g. GCC compiles both++andfetch_addtolock addon amd64.
– joe_chip
Aug 29 '18 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 tofetch_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 '18 at 12:14
i missed that dissucion is about atomic<int> and not an just int increment, my fault
– Artur Bac
Aug 29 '18 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 '18 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 '18 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 '18 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 '18 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 '18 at 12:14
i missed that dissucion is about atomic<int> and not an just int increment, my fault
– Artur Bac
Aug 29 '18 at 15:21
i missed that dissucion is about atomic<int> and not an just int increment, my fault
– Artur Bac
Aug 29 '18 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 '18 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 '18 at 15:27
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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

