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

Update AbstractNodeQueue.java to avoid redundant reads of head in peekNode #15596

Merged
merged 6 commits into from
Aug 18, 2014
Merged

Update AbstractNodeQueue.java to avoid redundant reads of head in peekNode #15596

merged 6 commits into from
Aug 18, 2014

Conversation

nitsanw
Copy link
Contributor

@nitsanw nitsanw commented Jul 28, 2014

Avoid spinning on head when a peek/poll observes a null next but the queue is not empty.

Avoid spinning on head when a peek/poll observes a null next but the queue is not empty.
@lightbend-cla-validator

Hi @nitsanw,

Thank you for your contribution! We really value the time you've taken to put this together.

Before we proceed with reviewing this pull request, please sign the Typesafe Contributors License Agreement:

http://www.typesafe.com/contribute/cla

Node<T> next = tail.next();
if (next == null && get() != tail) {
// if tail != head this is not going to change until consumer makes progress
// we can avoid reading the head and just spin on next until it shows the f**k up
Copy link
Member

Choose a reason for hiding this comment

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

Please refrain from obscenity in comments (even masked)

Copy link
Member

Choose a reason for hiding this comment

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

Even if the urge is irresistable :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

irresistable will mean I cannot do as you asked.... sad and confused am I :-(
can you replace f**k with Belgium?

Copy link
Member

Choose a reason for hiding this comment

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

Joke aside, please change the comment.

@akka-ci
Copy link

akka-ci commented Jul 28, 2014

Can one of the repo owners verify this patch?

@drewhk
Copy link
Member

drewhk commented Jul 28, 2014

OK TO TEST

@nitsanw nitsanw added validating and removed tested labels Jul 28, 2014
@rkuhn
Copy link
Contributor

rkuhn commented Jul 28, 2014

@nitsanw Have you benchmarked this change and found it to perform significantly better? It will probably help the queue, I’m wondering how that translates to the full Actor use-case.

language language....
@nitsanw nitsanw added validating and removed tested labels Jul 28, 2014
@akka-ci
Copy link

akka-ci commented Jul 28, 2014

Pull request validation: SUCCESS 👍
Refer to this link for build results: https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/400/

@nitsanw
Copy link
Contributor Author

nitsanw commented Jul 28, 2014

@rkuhn I have not benchmarked this change. I have benchmarked similar phenomena here: http://psy-lob-saw.blogspot.com/2014/06/jdk8-update-on-scalable-counters.html
Where the read impacts the mutators.

@nitsanw nitsanw added tested and removed validating labels Jul 28, 2014
@akka-ci
Copy link

akka-ci commented Jul 28, 2014

Pull request validation: SUCCESS 👍
Refer to this link for build results: https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/402/

@nitsanw
Copy link
Contributor Author

nitsanw commented Jul 28, 2014

Had a look at the Typesafe individual CLA. It involves printing and scanning and reading legal paperwork... I'm not going to find time for it. I'm currently working with Azul and if they have a CLA I can contribute through that one.
Please feel free to use the above code (have some authorised contributor copy and paste it if that works for you), with or without attribution :-)

@ktoso
Copy link
Member

ktoso commented Jul 28, 2014

Hey Nitsan :-)
You don't have to do any real-mail stuffs thank goodness: If you want to you can sign it and keep for yourself, but once you do step 2) on http://www.typesafe.com/contribute/cla we trust you signed it - and that's checked and verified online (via github account and our api).

Cheers!

@nitsanw
Copy link
Contributor Author

nitsanw commented Jul 29, 2014

@ktoso I wish it said that in big red letters!!! :-) my eyes lose focus very rapidly when faced with red tape.
I pushed the button in good faith, CLA blindly 'signed'.

}
return next;
}
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this assume that it's only ever the dequeuer that peeks?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume peek to be a consumer method. Is that not the usage intended? If peek is concurrent to poll that allows peek to return null when queue is not empty in both implementations as peekNode can return a node halfway through being polled.

Copy link
Member

Choose a reason for hiding this comment

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

Could you show me the state diagram for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:-) If we could get to a white board, sure. I'm a bit lame with drawing tools, let me try to convince you with text first.
Poll and peek both assume that a node found through tail.next() has a non-null value. This assumption is not challenged by the old or the new implementation.
Now poll() does this:
final Node next = peekNode(); // <- T0
if (next == null) return null;
else {
final T ret = next.value;
next.value = null; // <- T2
Unsafe.instance.putOrderedObject(this, tailOffset, next);
return ret;
}
While peek does this:
final Node n = peekNode(); // <- T1
return (n != null) ? n.value : null;// <- T3
At T1 n is not null, but by T3 n.value is null.

Copy link
Member

Choose a reason for hiding this comment

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

Of course! Thanks for reminding me, I knew that there was something I had not thought about.

Copy link
Member

Choose a reason for hiding this comment

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

Aside from peek() which is not used, it's only isEmpty() which calls peekNode()

peek() and peekNode() should be documented to only be called by the consumer. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There you go...

Copy link
Contributor

Choose a reason for hiding this comment

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

Just for completeness’ sake: in Akka peekNode is also called by the enqueuer, but that is fine since that one will see all the right things (it peeks into its own store buffer); third parties may erroneously report null but that is not an issue for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rkuhn calling peekNode from any thread other than the consumer is not fine, as described above. Did I miss something about the producer which somehow negates this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, in general it is not fine, but I am pretty sure that Akka’s particular usage is fine. Sorry for being not 100% clear on that.

@akka-ci
Copy link

akka-ci commented Aug 8, 2014

Pull request validation: FAILED 👎
Refer to this link for build results: https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/449/

@@ -53,17 +61,19 @@ public final void addNode(final Node<T> n) {
}

public final boolean isEmpty() {
return peek() == null;
return Unsafe.instance.getObjectVolatile(this, tailOffset)) == get();
Copy link
Member

Choose a reason for hiding this comment

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

does not compile:

[error] /localhome/jenkinsakka/workspace/pr-validator-per-commit-jenkins/akka-actor/src/main/java/akka/dispatch/AbstractNodeQueue.java:64: ';' expected
[error] return Unsafe.instance.getObjectVolatile(this, tailOffset)) == get();
[error] ^
[error] 1 error

Should be:

 return Unsafe.instance.getObjectVolatile(this, tailOffset) == get();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@viktorklang
Copy link
Member

LGTM, waiting for PR validator to run

@akka-ci
Copy link

akka-ci commented Aug 8, 2014

Pull request validation: SUCCESS 👍
Refer to this link for build results: https://jenkins.akka.io/job/pr-validator-per-commit-jenkins/450/

normanmaurer pushed a commit to netty/netty that referenced this pull request Aug 18, 2014
Motivation:

There is not need todo redunant reads of head in peakNode as we can just spin on next() until it becomes visible.

Modifications:

Remove redundant reads of head in peakNode. This is based on @nitsanw's patch for akka.
See akka/akka#15596

Result:

Less volatile access.
@rkuhn
Copy link
Contributor

rkuhn commented Aug 18, 2014

Thanks a lot @nitsanw!

rkuhn added a commit that referenced this pull request Aug 18, 2014
Update AbstractNodeQueue.java to avoid redundant reads of head in peekNode
@rkuhn rkuhn merged commit 4ce7766 into akka:master Aug 18, 2014
normanmaurer pushed a commit to netty/netty that referenced this pull request Aug 21, 2014
Motivation:

There is not need todo redunant reads of head in peakNode as we can just spin on next() until it becomes visible.

Modifications:

Remove redundant reads of head in peakNode. This is based on @nitsanw's patch for akka.
See akka/akka#15596

Result:

Less volatile access.
normanmaurer pushed a commit to netty/netty that referenced this pull request Aug 21, 2014
Motivation:

There is not need todo redunant reads of head in peakNode as we can just spin on next() until it becomes visible.

Modifications:

Remove redundant reads of head in peakNode. This is based on @nitsanw's patch for akka.
See akka/akka#15596

Result:

Less volatile access.
normanmaurer pushed a commit to netty/netty that referenced this pull request Aug 21, 2014
Motivation:

There is not need todo redunant reads of head in peakNode as we can just spin on next() until it becomes visible.

Modifications:

Remove redundant reads of head in peakNode. This is based on @nitsanw's patch for akka.
See akka/akka#15596

Result:

Less volatile access.
@nomagic
Copy link

nomagic commented Sep 8, 2015

Shouldn't the comment read:
if tail != head this is not going to change until consumer the producer makes progress.
It's the producer that has yet to update node.next (hence the loop)
No code change, just left me confused

@nitsanw
Copy link
Contributor Author

nitsanw commented Sep 8, 2015

@nomagic you are correct :)

@rkuhn
Copy link
Contributor

rkuhn commented Sep 8, 2015

Indeed; do you want to submit a PR? (read: thanks for spotting this!)

@nomagic
Copy link

nomagic commented Sep 15, 2015

Doesn't seem worth it for a comment change.

BTW, You're probably aware of it, but just in case:

 // Extends AtomicReference for the "head" slot (which is the one that is appended to) since Unsafe
does not expose XCHG operation intrinsically

This is no longer true, was fixed in http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7023898, at least on latest jdk 8 builds, x86_64 emits an XCHG (Yay!!)

@nomagic
Copy link

nomagic commented Oct 1, 2015

@rkuhn Yeah, my comment about Unsafe.getAndSet being intrinsified was more aimed at providing the "opportunity" to have AbstractNodeQueue decoupled from directly extending AtomicReference; I don't know if it's worth it (obviously would get a small memory gain at least), but then again admonitions about "premature optimization" probably don't apply here

@rkuhn
Copy link
Contributor

rkuhn commented Oct 1, 2015

AtomicReference has just a single field, so it should not make a difference whether we extend that or inline the field, or should it?

@viktorklang
Copy link
Member

@rkuhn AFAIK, that's implementation (JVM) dependent. Any smart (read. production worthy) JVM is going to make inheritance cost 0 extra memory.

@nomagic
Copy link

nomagic commented Oct 1, 2015

It's hard to know frankly, but you guys are going through the trouble of accessing _tail via Unsafe (I wouldn't have just automatically assumed that's any faster than a direct volatile read/write - but I'm assuming you guys know better).
The thing I don't fully understand is exactly what has been intrinsified, i.e. is it just Unsafe.getAndSetObject() or is AtomicReference.getAndSet itself being intrinsified;I had always assumed that it was just the Unsafe methods that were intrinsified, but that bug report I cited suggests otherwise.
The motivation for my bringing your attention to this was the original code comment, that suggested:

  1. since the JVM isn't intrinsifying (i.e. XCHG'ng) Unsafe.getAndSet, then
  2. might as well extend AtomicReference

which makes sense.
But since 1 no longer holds, then 2 doesn't follow

@rkuhn
Copy link
Contributor

rkuhn commented Oct 1, 2015

Right, it wouldn’t be necessary anymore to inherit on JDK8, but there is also no downside to it that I can see, so no reason to change. But I may of course overlook something.

@nomagic
Copy link

nomagic commented Oct 1, 2015

And it should be pointed out, that if you look all through j.u.c code, AtomicReference is definitely being eschewed.
Now, that doesn't mean it should be, but I generally presume Doug Lea and co know what they're doing

@nomagic
Copy link

nomagic commented Oct 1, 2015

@rkuhn just to be clear, it was never necessary (AtomicReference just invokes Unsafe), it was just convenient;
i.e. it wouldn't break backwards compatibility to use Unsafe directly

pulllock pushed a commit to pulllock/netty that referenced this pull request Oct 19, 2023
Motivation:

There is not need todo redunant reads of head in peakNode as we can just spin on next() until it becomes visible.

Modifications:

Remove redundant reads of head in peakNode. This is based on @nitsanw's patch for akka.
See akka/akka#15596

Result:

Less volatile access.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Issues that the core team will likely not have time to work on t:core tested PR that was successfully built and tested by Jenkins
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants