Setting an AtomicBoolean again
up vote
4
down vote
favorite
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
add a comment |
up vote
4
down vote
favorite
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
add a comment |
up vote
4
down vote
favorite
up vote
4
down vote
favorite
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
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
java multithreading caching concurrency volatile
edited Nov 22 at 17:07
asked Nov 22 at 16:26
OhNoOhYes
233
233
add a comment |
add a comment |
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)
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)
orb.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
add a comment |
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.
Why does a no-opCAS
also touch the cache line? For examplecompareAndSet(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
add a comment |
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)
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)
orb.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
add a comment |
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)
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)
orb.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
add a comment |
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)
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)
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)
orb.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
add a comment |
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)
orb.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
add a comment |
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.
Why does a no-opCAS
also touch the cache line? For examplecompareAndSet(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
add a comment |
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.
Why does a no-opCAS
also touch the cache line? For examplecompareAndSet(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
add a comment |
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.
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.
answered Nov 22 at 16:33
Peter Lawrey
439k55556958
439k55556958
Why does a no-opCAS
also touch the cache line? For examplecompareAndSet(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
add a comment |
Why does a no-opCAS
also touch the cache line? For examplecompareAndSet(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
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.
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.
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%2f53434995%2fsetting-an-atomicboolean-again%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