-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Conversation
Avoid spinning on head when a peek/poll observes a null next but the queue is not empty.
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: |
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 |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Can one of the repo owners verify this patch? |
OK TO TEST |
@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....
Pull request validation: SUCCESS 👍 |
@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 |
Pull request validation: SUCCESS 👍 |
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. |
Hey Nitsan :-) Cheers! |
@ktoso I wish it said that in big red letters!!! :-) my eyes lose focus very rapidly when faced with red tape. |
} | ||
return next; | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There you go...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Pull request validation: FAILED 👎 |
@@ -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(); |
There was a problem hiding this comment.
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();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
LGTM, waiting for PR validator to run |
Pull request validation: SUCCESS 👍 |
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.
Thanks a lot @nitsanw! |
Update AbstractNodeQueue.java to avoid redundant reads of head in peekNode
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.
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.
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.
Shouldn't the comment read: |
@nomagic you are correct :) |
Indeed; do you want to submit a PR? (read: thanks for spotting this!) |
Doesn't seem worth it for a comment change. BTW, You're probably aware of it, but just in case:
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!!) |
@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 |
AtomicReference has just a single field, so it should not make a difference whether we extend that or inline the field, or should it? |
@rkuhn AFAIK, that's implementation (JVM) dependent. Any smart (read. production worthy) JVM is going to make inheritance cost 0 extra memory. |
It's hard to know frankly, but you guys are going through the trouble of accessing
which makes sense. |
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. |
And it should be pointed out, that if you look all through |
@rkuhn just to be clear, it was never necessary ( |
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.
Avoid spinning on head when a peek/poll observes a null next but the queue is not empty.