what is the difference between ++, add operation and fetch_add() in atomic()
Clash Royale CLAN TAG#URR8PPP
up vote
19
down vote
favorite
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 |Â
up vote
19
down vote
favorite
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 |Â
up vote
19
down vote
favorite
up vote
19
down vote
favorite
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
asked Aug 29 at 5:04
Alok
7671720
7671720
add a comment |Â
add a comment |Â
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.
1
According to the standardcnt.fetch_add(1)
is equivalent tocnt++
.
â 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 iscnt++
. The code is not acting on the return value.
â Remy Lebeau
Aug 29 at 7:59
add a comment |Â
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.
add a comment |Â
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]
3
Are you talking aboutoperator++
ofstd::atomic
? It is supposed to be atomic. E. g. GCC compiles both++
andfetch_add
tolock 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 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 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
add a comment |Â
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.
1
According to the standardcnt.fetch_add(1)
is equivalent tocnt++
.
â 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 iscnt++
. The code is not acting on the return value.
â Remy Lebeau
Aug 29 at 7:59
add a comment |Â
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.
1
According to the standardcnt.fetch_add(1)
is equivalent tocnt++
.
â 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 iscnt++
. The code is not acting on the return value.
â Remy Lebeau
Aug 29 at 7:59
add a comment |Â
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.
++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 at 5:27
answered Aug 29 at 5:10
Remy Lebeau
318k17234415
318k17234415
1
According to the standardcnt.fetch_add(1)
is equivalent tocnt++
.
â 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 iscnt++
. The code is not acting on the return value.
â Remy Lebeau
Aug 29 at 7:59
add a comment |Â
1
According to the standardcnt.fetch_add(1)
is equivalent tocnt++
.
â 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 iscnt++
. 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
add a comment |Â
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.
add a comment |Â
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.
add a comment |Â
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.
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 at 5:27
answered Aug 29 at 5:11
taskinoor
38.1k794122
38.1k794122
add a comment |Â
add a comment |Â
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]
3
Are you talking aboutoperator++
ofstd::atomic
? It is supposed to be atomic. E. g. GCC compiles both++
andfetch_add
tolock 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 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 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
add a comment |Â
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]
3
Are you talking aboutoperator++
ofstd::atomic
? It is supposed to be atomic. E. g. GCC compiles both++
andfetch_add
tolock 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 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 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
add a comment |Â
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]
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 at 9:19
Artur Bac
706
706
3
Are you talking aboutoperator++
ofstd::atomic
? It is supposed to be atomic. E. g. GCC compiles both++
andfetch_add
tolock 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 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 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
add a comment |Â
3
Are you talking aboutoperator++
ofstd::atomic
? It is supposed to be atomic. E. g. GCC compiles both++
andfetch_add
tolock 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 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 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
add a comment |Â
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
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
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
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
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