Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the PaddedAtomicLong #264

Merged
merged 2 commits into from Nov 26, 2020
Merged

Conversation

pveentjer
Copy link
Contributor

@pveentjer pveentjer commented Sep 13, 2019

A padded version of the java.util.atomic.AtomicLong.

@franz1981
Copy link
Collaborator

Thanks for the contribution! Let me know when it can be reviewed :)

Copy link
Contributor

@nitsanw nitsanw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution. Please consider the comments. As mentioned this needs some consideration from a compatibility standpoint, or might need to be handling similar to MPSC linked queue CAS method.

* @see AtomicLong#getAndIncrement()
*/
public final long getAndIncrement() {
return UNSAFE.getAndAddLong(this, VALUE_OFFSET, 1L);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means no pre-8 compatibility, which is not necessarily bad, but needs consideration.

Copy link
Contributor Author

@pveentjer pveentjer Sep 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look at the Java 6 code

 public final long getAndAdd(long delta) {
        while (true) {
            long current = get();
            long next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

So Unsafe.getAndSetLong has been replaced by something I expect to be a lot slower.

I made quick JMH benchmark:

@Fork(value = 2)
@Warmup(iterations = 1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Threads(4)
public class GetAndAddBenchmark {

    public AtomicLong a;

    @Setup
    public void setup(){
        a=new AtomicLong();
    }

    @Benchmark
    public long slow() {
        long delta = 10;
        while (true) {
            long current = a.get();
            long next = current + delta;
            if (a.compareAndSet(current, next))
                return current;
        }

    }

    @Benchmark
    public long fast() {
        long delta = 10;
        return a.getAndAdd(delta);
    }
}

And there is a significant performance loss with the slow approach:

Benchmark                     Mode  Cnt    Score    Error  Units
GetAndAddBenchmark.fast       avgt   10  118.111 ±  9.630  ns/op
GetAndAddBenchmark.slow       avgt   10  231.833 ± 30.669  ns/op

I had a peek at the generated Assembly (perfasm) and key difference is that the slow approach does a volatile read and then a 'lock cmpxchg' and the fast approach only needs a 'lock xadd'. The issue is that the former approach can run into a cas failure, while this can't happen with the lock xadd (hence latter approach is more performant).

In other words: there is a very significant performance loss if I would do it in the Java 6 way. Do you see any way to do it in a Java 6 compatible way but without the performance impact? One option would be to add a switch that chooses the appropriate logic based on the existence of Unsafe.getAndSetLong

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nitsanw could you have another look?

@coveralls
Copy link

coveralls commented Sep 16, 2019

Pull Request Test Coverage Report for Build 717

  • 48 of 54 (88.89%) changed or added relevant lines in 1 file are covered.
  • 30 unchanged lines in 6 files lost coverage.
  • Overall coverage decreased (-0.4%) to 85.831%

Changes Missing Coverage Covered Lines Changed/Added Lines %
jctools-core/src/main/java/org/jctools/util/PaddedAtomicLong.java 48 54 88.89%
Files with Coverage Reduction New Missed Lines %
jctools-core/src/main/java/org/jctools/maps/NonBlockingSetInt.java 1 77.09%
jctools-core/src/main/java/org/jctools/queues/MpmcUnboundedXaddArrayQueue.java 2 96.06%
jctools-core/src/main/java/org/jctools/maps/NonBlockingHashMap.java 4 78.38%
jctools-core/src/main/java/org/jctools/maps/NonBlockingHashMapLong.java 6 78.64%
jctools-core/src/main/java/org/jctools/maps/NonBlockingIdentityHashMap.java 6 76.31%
jctools-core/src/main/java/org/jctools/maps/ConcurrentAutoTable.java 11 47.69%
Totals Coverage Status
Change from base Build 716: -0.4%
Covered Lines: 4822
Relevant Lines: 5618

💛 - Coveralls

@franz1981
Copy link
Collaborator

@pveentjer
Look at https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/MpscLinkedQueue.java#L43

Probably for java 6 we don't have other options if not to accept it to be slower :)

@nitsanw
Copy link
Contributor

nitsanw commented Nov 4, 2019

@pveentjer I've pushed to your branch, please have a look, if it's all good please submit a unit test and rebase + squash commits to clean merge.
Sorry for the slow responses, and thanks for your contribution!

@pveentjer
Copy link
Contributor Author

@nitsanw I'll get my act together today and finish up the work.

Once that is done I'll also add an PaddedAtomicReference.

@pveentjer
Copy link
Contributor Author

I have added a unit tests and squashed the commits.

@pveentjer pveentjer changed the title WIP: Added the PaddedAtomicLong Added the PaddedAtomicLong Dec 29, 2019
@franz1981
Copy link
Collaborator

franz1981 commented Dec 29, 2019

@pveentjer There is a build failure on the CI
A couple of notes:

  • getAndSet0 and getAndAdd0 could be copied directly on the callers, to save inlining budget
  • it would be nice to improve UnsafeAccess to allow forcing the cmpAndSwap versions of getAndSet, both for benchmarking purposes and to increase coverage while using jdk 8

@nitsanw
Copy link
Contributor

nitsanw commented Dec 30, 2019

[2019-12-29 11:42:41] [autobuild] [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project jctools-core: Compilation failure: Compilation failure: 
[2019-12-29 11:42:41] [autobuild] [ERROR] /opt/src/jctools-core/src/main/java/org/jctools/util/PaddedAtomicLong.java:[322,25] cannot find symbol
[2019-12-29 11:42:41] [autobuild] [ERROR]   symbol:   variable SUPPORTS_GET_AND_SET
[2019-12-29 11:42:41] [autobuild] [ERROR]   location: class org.jctools.util.UnsafeAccess
[2019-12-29 11:42:41] [autobuild] [ERROR] /opt/src/jctools-core/src/main/java/org/jctools/util/PaddedAtomicLong.java:[322,12] illegal start of type
[2019-12-29 11:42:41] [autobuild] [ERROR] /opt/src/jctools-core/src/main/java/org/jctools/util/PaddedAtomicLong.java:[340,25] cannot find symbol
[2019-12-29 11:42:41] [autobuild] [ERROR]   symbol:   variable SUPPORTS_GET_AND_SET

@nitsanw
Copy link
Contributor

nitsanw commented Dec 31, 2019

@pveentjer currently the build is failing, looks like you need to rebase on top of latest code and adjust. If you are quick we might be able to ship this with the 3.0 release this week.

@pveentjer
Copy link
Contributor Author

pveentjer commented Oct 16, 2020

Could someone have another look at this PR?

I'm not sure of copying the content of the methods is the right approach because it increases code duplication and it isn't trivial code.

abstract class PaddedAtomicLongL1Pad extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1;

transient long p01, p02, p03, p04, p05, p06, p07;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use byte padding as per the rest of JCTools

Copy link
Contributor Author

@pveentjer pveentjer Oct 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Out of curiosity, is there any difference compared to using longs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from JDK 15 the JVM started reordering fields to fill in gaps between object header and fields, resulting in long based padding fails.

@nitsanw
Copy link
Contributor

nitsanw commented Nov 26, 2020

The PR fits in with other work, I'm going to merge and work out the remaining bits. Thanks for the help @pveentjer !

@nitsanw nitsanw merged commit bd4ac47 into JCTools:master Nov 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants