hyde
hyde

Reputation: 62906

AtomicInteger.incrementAndGet() vs. AtomicInteger.getAndIncrement()

When return value is not of interest, is there any (even irrelevant in practice) difference between AtomicInteger.getAndIncrement() and AtomicInteger.incrementAndGet() methods, when return value is ignored?

I'm thinking of differences like which would be more idiomatic, as well as which would put less load in CPU caches getting synchronized, or anything else really, anything to help decide which one to use more rationally than tossing a coin.

Upvotes: 65

Views: 35610

Answers (6)

Peter Cordes
Peter Cordes

Reputation: 366066

All else equal, prefer getAndIncrement()

Some CPUs support it directly (even when the return address is used), unlike incrementAndGet(), so you might save one inc reg instruction if the return value is used.


The portable CAS retry loop isn't the implementation actually used on real JVMs for mainstream ISAs that have instructions for this. At least I hope not!

If your JVM is any good, these will compile + JIT to a single instruction on x86 or ARMv8.1.

If the return value isn't used, either can be x86 lock inc dword [mem]

If the return value is used (or the optimizer doesn't know this peephole), getAndIncrement() is the same operation as C++ fetch_add(1), which is x86 lock xadd [mem], reg (exchange-and-add) or ARMv8.1 ldadd (or ldaddal for the seq_cst version).

There is no single-instruction IncAndGet; you need a separate add or inc reg if you want that. But if you don't use the return value, hopefully a compiler can still omit that even if it uses lock xadd instead of

For example in C:

#include <stdatomic.h>

void Inc(_Atomic int *p) {
    ++*p;
}

int GetAndInc(_Atomic int *p) {
    //return atomic_fetch_add_explicit(p, 1, memory_order_relaxed);
    return (*p)++;        // uses seq_cst like Java is stuck with
}

int IncAndGet(_Atomic int *p) {
    //return 1 + atomic_fetch_add_explicit(p, 1, memory_order_relaxed);
    return ++*p;
}

AArch64 assembly (from Godbolt, Clang 19 -O2 -mcpu=cortex-a77)

Inc:
        mov     w8, #1
        ldaddal w8, w8, [x0]   // Load+ADD  Acquire reLease
        ret

GetAndInc:
        mov     w8, #1
        ldaddal w8, w0, [x0]
        ret

IncAndGet:
        mov     w8, #1
        ldaddal w8, w8, [x0]
        add     w0, w8, #1     // turn fetch_add result into add_fetch
        ret

Same for x86-64

Inc:
        lock            inc     dword ptr [rdi]
        ret

GetAndInc:
        mov     eax, 1
        lock            xadd    dword ptr [rdi], eax
        ret

IncAndGet:
        mov     eax, 1
        lock            xadd    dword ptr [rdi], eax
        inc     eax
        ret

On ISAs without single-instruction RMWs, like ARMv7 and ARMv8.0, or MIPS or PowerPC or I think RISC-V, there's no difference, or if anything incrementAndGet needs fewer spare registers since it can overwrite the load result with the add result. Same number of instructions either way for ARMv8.0 when told to inline the old-style instructions instead of calling libatomic helper functions in the hope that ARMv8.1 instructions will be available at run-time even if we don't want to require them (-mno-outline-atomics = inline atomics). These functions aren't running short on registers, only limited by needing the return value in the same register as the incoming pointer.

# AArch64 clang -O2 -Wall -mcpu=cortex-a53 -mno-outline-atomics
GetAndInc:
.LBB1_1:
        ldaxr   w8, [x0]      // load-acquire exclusive
        add     w9, w8, #1      // add into a different register
        stlxr   w10, w9, [x0] // store-release exclusive
        cbnz    w10, .LBB1_1
        mov     w0, w8
        ret

IncAndGet:
.LBB2_1:
        ldaxr   w8, [x0]
        add     w8, w8, #1     // overwrite the load result
        stlxr   w9, w8, [x0]
        cbnz    w9, .LBB2_1
        mov     w0, w8
        ret

My recommendation at the top of the answer to favour getAndIncrement() is based on the fact that if an ISA provides a single-instruction atomic, it's normally a fetch_add not an add_fetch. And that is the case on the two most common mainstream ISAs, x86-64 and AArch64.


as well as which would put less load in CPU caches getting synchronized

Absolutely no difference. The atomic RMW is the same either way, it's just a question of whether you need to fix-up the result for local use.


Fun fact: GNU C __atomic builtins also come in both flavours, __atomic_fetch_add and __atomic_add_fetch. (https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html#index-_005f_005fatomic_005fadd_005ffetch).

Upvotes: 1

Iłya Bursov
Iłya Bursov

Reputation: 24229

Just want to add to existing answers: there could be very small non-noticeable difference.

If you look at this implementation:

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

Note - both function call exactly the same function getAndAddInt, except +1 part, which means that in this implementation getAndIncrement is faster.

But, here is older implementation:

public final int getAndIncrement() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return current;
    }
}

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

The only difference is return variable, so both functions perform exactly the same.

Upvotes: 9

Ajju_bhai
Ajju_bhai

Reputation: 19

Here I am giving an example. Hope it will clear your doubt.

Suppose I have a variable i as

AtomicInteger i = new AtomicInteger();

In this case:

i.getAndIncrement() <==> i++;

And

i.incrementAndGet() <==> ++i;

Please have a look of the below programs

public class Test1
{   
    public static void main(String[] args) 
    {               
        AtomicInteger i = new AtomicInteger();
        System.out.println(i.incrementAndGet());  
        System.out.println(i);  

    }

}

**output

1 1 ======================================**

public class Test2
{   
    public static void main(String[] args) 
    {               
        AtomicInteger i = new AtomicInteger();
        System.out.println(i.getAndIncrement());  
        System.out.println(i); 

    }

}

**output

0 1 -------------**

Comment: 1) In the class Test1, incrementAndGet() will first increment the i value and then print.

2) In the class Test2, getAndIncrement() will first print the i value and then increment.

That's all.

Upvotes: 0

hyde
hyde

Reputation: 62906

Since no answer to the actual question has been given, here's my personal opinion based on the other answers (thanks, upvoted) and Java convention:

incrementAndGet()

is better, because method names should start with the verb describing the action, and intended action here is to increment only.

Starting with verb is the common Java convention, also described by official docs:

"Methods should be verbs, in mixed case with the first letter lowercase, with the first letter of each internal word capitalized."

Upvotes: 45

Joachim Sauer
Joachim Sauer

Reputation: 308269

No, there's no difference (if you don't care about the return value).

The code of those methods (in the OpenJDK) differs only in that one uses return next and the other uses return current.

Both use compareAndSet under the hood with the exact same algorithm. Both need to know both the old and the new value.

Upvotes: 11

assylias
assylias

Reputation: 328923

The code is essentially the same so it does not matter:

public final int getAndIncrement() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return current;
    }
}

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

Upvotes: 36

Related Questions