Warning: the question is a little long, but the part below the separation line is for curiosity only.
Oracle's JDK 7 implementation of AtomicInteger includes the following methods:
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta; // Only difference
if (compareAndSet(current, next))
return next;
}
}
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1; // Only difference
if (compareAndSet(current, next))
return next;
}
}
It seems clear that the second method could have been written:
public final int incrementAndGet() {
return addAndGet(1);
}
There are several other examples of similar code duplication in that class. I can't think of any reasons to do that but performance considerations (*). And I am pretty sure the authors did some in-depth testing before settling on that design.
Why (or in what circumstances) would the first code perform better than the second?
(*) I could not resist but write a quick micro benchmark. It shows (post-JIT) a systematic gap of 2-4% performance in favour of addAndGet(1)
vs incrementAndGet()
(that is admittedly small, but it is very consistent). I can't really explain that result either to be honest...
Output:
incrementAndGet(): 905
addAndGet(1): 868
incrementAndGet(): 902
addAndGet(1): 863
incrementAndGet(): 891
addAndGet(1): 867
...
Code:
public static void main(String[] args) throws Exception {
final int size = 100_000_000;
long start, end;
AtomicInteger ai;
System.out.println("JVM warmup");
for (int j = 0; j < 10; j++) {
start = System.nanoTime();
ai = new AtomicInteger();
for (int i = 0; i < size / 10; i++) {
ai.addAndGet(1);
}
end = System.nanoTime();
System.out.println("addAndGet(1): " + ((end - start) / 1_000_000));
start = System.nanoTime();
ai = new AtomicInteger();
for (int i = 0; i < size / 10; i++) {
ai.incrementAndGet();
}
end = System.nanoTime();
System.out.println("incrementAndGet(): " + ((end - start) / 1_000_000));
}
System.out.println("\nStart measuring\n");
for (int j = 0; j < 10; j++) {
start = System.nanoTime();
ai = new AtomicInteger();
for (int i = 0; i < size; i++) {
ai.incrementAndGet();
}
end = System.nanoTime();
System.out.println("incrementAndGet(): " + ((end - start) / 1_000_000));
start = System.nanoTime();
ai = new AtomicInteger();
for (int i = 0; i < size; i++) {
ai.addAndGet(1);
}
end = System.nanoTime();
System.out.println("addAndGet(1): " + ((end - start) / 1_000_000));
}
}
An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer . However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes.
So,AtomicInteger uses Volatile inside.
Yes, you are correct. AtomicInteger would not grant any benefit if all access to the object is synchronized (only one thread, at most, would be accessing its contents at any given moment).
AtomicInteger class provides operations on underlying int value that can be read and written atomically, and also contains advanced atomic operations. AtomicInteger supports atomic operations on underlying int variable. It have get and set methods that work like reads and writes on volatile variables.
I'll give new supposition. If we look into byte code of AtomicInteger
we will see, that the main difference between them is that addAndGet
uses iload_
instruction, and incrementAndGet
uses iconst_
instruction:
public final int addAndGet(int);
...
4: istore_2
5: iload_2
6: iload_1
7: iadd
public final int incrementAndGet();
...
4: istore_1
5: iload_1
6: iconst_1
7: iadd
It seems, that iconst_
+iadd
translates as INC
instruction, due to iload_
...iadd
as ADD
instruction. This all relates to commonly known question about ADD 1
vs INC
and so on:
Relative performance of x86 inc vs. add instruction
Is ADD 1 really faster than INC ? x86
This could be the answer, why addAndGet
is slightly faster than incrementAndGet
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With