8192943: Optimize atomic accumulators using VarHandle getAndSet

Reviewed-by: martin, psandoz, chegar
This commit is contained in:
Doug Lea 2017-12-08 15:26:56 -08:00
parent 0b3b384a27
commit 00d1900dc9
5 changed files with 113 additions and 117 deletions

View file

@ -105,20 +105,20 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
* @param x the value * @param x the value
*/ */
public void accumulate(double x) { public void accumulate(double x) {
Cell[] as; long b, v, r; int m; Cell a; Cell[] cs; long b, v, r; int m; Cell c;
if ((as = cells) != null if ((cs = cells) != null
|| ((r = doubleToRawLongBits || ((r = doubleToRawLongBits
(function.applyAsDouble(longBitsToDouble(b = base), x))) != b (function.applyAsDouble(longBitsToDouble(b = base), x))) != b
&& !casBase(b, r))) { && !casBase(b, r))) {
boolean uncontended = true; boolean uncontended = true;
if (as == null if (cs == null
|| (m = as.length - 1) < 0 || (m = cs.length - 1) < 0
|| (a = as[getProbe() & m]) == null || (c = cs[getProbe() & m]) == null
|| !(uncontended = || !(uncontended =
((r = doubleToRawLongBits ((r = doubleToRawLongBits
(function.applyAsDouble (function.applyAsDouble
(longBitsToDouble(v = a.value), x))) == v) (longBitsToDouble(v = c.value), x))) == v)
|| a.cas(v, r))) || c.cas(v, r)))
doubleAccumulate(x, function, uncontended); doubleAccumulate(x, function, uncontended);
} }
} }
@ -133,13 +133,13 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
* @return the current value * @return the current value
*/ */
public double get() { public double get() {
Cell[] as = cells; Cell[] cs = cells;
double result = longBitsToDouble(base); double result = longBitsToDouble(base);
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
result = function.applyAsDouble result = function.applyAsDouble
(result, longBitsToDouble(a.value)); (result, longBitsToDouble(c.value));
} }
return result; return result;
} }
@ -153,12 +153,12 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
* updating. * updating.
*/ */
public void reset() { public void reset() {
Cell[] as = cells; Cell[] cs = cells;
base = identity; base = identity;
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
a.reset(identity); c.reset(identity);
} }
} }
@ -173,14 +173,12 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
* @return the value before reset * @return the value before reset
*/ */
public double getThenReset() { public double getThenReset() {
Cell[] as = cells; Cell[] cs = cells;
double result = longBitsToDouble(base); double result = longBitsToDouble(getAndSetBase(identity));
base = identity; if (cs != null) {
if (as != null) { for (Cell c : cs) {
for (Cell a : as) { if (c != null) {
if (a != null) { double v = longBitsToDouble(c.getAndSet(identity));
double v = longBitsToDouble(a.value);
a.reset(identity);
result = function.applyAsDouble(result, v); result = function.applyAsDouble(result, v);
} }
} }

View file

@ -87,15 +87,15 @@ public class DoubleAdder extends Striped64 implements Serializable {
* @param x the value to add * @param x the value to add
*/ */
public void add(double x) { public void add(double x) {
Cell[] as; long b, v; int m; Cell a; Cell[] cs; long b, v; int m; Cell c;
if ((as = cells) != null || if ((cs = cells) != null ||
!casBase(b = base, !casBase(b = base,
Double.doubleToRawLongBits Double.doubleToRawLongBits
(Double.longBitsToDouble(b) + x))) { (Double.longBitsToDouble(b) + x))) {
boolean uncontended = true; boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 || if (cs == null || (m = cs.length - 1) < 0 ||
(a = as[getProbe() & m]) == null || (c = cs[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, !(uncontended = c.cas(v = c.value,
Double.doubleToRawLongBits Double.doubleToRawLongBits
(Double.longBitsToDouble(v) + x)))) (Double.longBitsToDouble(v) + x))))
doubleAccumulate(x, null, uncontended); doubleAccumulate(x, null, uncontended);
@ -115,12 +115,12 @@ public class DoubleAdder extends Striped64 implements Serializable {
* @return the sum * @return the sum
*/ */
public double sum() { public double sum() {
Cell[] as = cells; Cell[] cs = cells;
double sum = Double.longBitsToDouble(base); double sum = Double.longBitsToDouble(base);
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
sum += Double.longBitsToDouble(a.value); sum += Double.longBitsToDouble(c.value);
} }
return sum; return sum;
} }
@ -133,12 +133,12 @@ public class DoubleAdder extends Striped64 implements Serializable {
* known that no threads are concurrently updating. * known that no threads are concurrently updating.
*/ */
public void reset() { public void reset() {
Cell[] as = cells; Cell[] cs = cells;
base = 0L; // relies on fact that double 0 must have same rep as long base = 0L; // relies on fact that double 0 must have same rep as long
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
a.reset(); c.reset();
} }
} }
@ -153,16 +153,12 @@ public class DoubleAdder extends Striped64 implements Serializable {
* @return the sum * @return the sum
*/ */
public double sumThenReset() { public double sumThenReset() {
Cell[] as = cells; Cell[] cs = cells;
double sum = Double.longBitsToDouble(base); double sum = Double.longBitsToDouble(getAndSetBase(0L));
base = 0L; if (cs != null) {
if (as != null) { for (Cell c : cs) {
for (Cell a : as) { if (c != null)
if (a != null) { sum += Double.longBitsToDouble(c.getAndSet(0L));
long v = a.value;
a.reset();
sum += Double.longBitsToDouble(v);
}
} }
} }
return sum; return sum;

View file

@ -103,17 +103,17 @@ public class LongAccumulator extends Striped64 implements Serializable {
* @param x the value * @param x the value
*/ */
public void accumulate(long x) { public void accumulate(long x) {
Cell[] as; long b, v, r; int m; Cell a; Cell[] cs; long b, v, r; int m; Cell c;
if ((as = cells) != null if ((cs = cells) != null
|| ((r = function.applyAsLong(b = base, x)) != b || ((r = function.applyAsLong(b = base, x)) != b
&& !casBase(b, r))) { && !casBase(b, r))) {
boolean uncontended = true; boolean uncontended = true;
if (as == null if (cs == null
|| (m = as.length - 1) < 0 || (m = cs.length - 1) < 0
|| (a = as[getProbe() & m]) == null || (c = cs[getProbe() & m]) == null
|| !(uncontended = || !(uncontended =
(r = function.applyAsLong(v = a.value, x)) == v (r = function.applyAsLong(v = c.value, x)) == v
|| a.cas(v, r))) || c.cas(v, r)))
longAccumulate(x, function, uncontended); longAccumulate(x, function, uncontended);
} }
} }
@ -128,12 +128,12 @@ public class LongAccumulator extends Striped64 implements Serializable {
* @return the current value * @return the current value
*/ */
public long get() { public long get() {
Cell[] as = cells; Cell[] cs = cells;
long result = base; long result = base;
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
result = function.applyAsLong(result, a.value); result = function.applyAsLong(result, c.value);
} }
return result; return result;
} }
@ -147,12 +147,12 @@ public class LongAccumulator extends Striped64 implements Serializable {
* updating. * updating.
*/ */
public void reset() { public void reset() {
Cell[] as = cells; Cell[] cs = cells;
base = identity; base = identity;
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
a.reset(identity); c.reset(identity);
} }
} }
@ -167,14 +167,12 @@ public class LongAccumulator extends Striped64 implements Serializable {
* @return the value before reset * @return the value before reset
*/ */
public long getThenReset() { public long getThenReset() {
Cell[] as = cells; Cell[] cs = cells;
long result = base; long result = getAndSetBase(identity);
base = identity; if (cs != null) {
if (as != null) { for (Cell c : cs) {
for (Cell a : as) { if (c != null) {
if (a != null) { long v = c.getAndSet(identity);
long v = a.value;
a.reset(identity);
result = function.applyAsLong(result, v); result = function.applyAsLong(result, v);
} }
} }

View file

@ -83,12 +83,12 @@ public class LongAdder extends Striped64 implements Serializable {
* @param x the value to add * @param x the value to add
*/ */
public void add(long x) { public void add(long x) {
Cell[] as; long b, v; int m; Cell a; Cell[] cs; long b, v; int m; Cell c;
if ((as = cells) != null || !casBase(b = base, b + x)) { if ((cs = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true; boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 || if (cs == null || (m = cs.length - 1) < 0 ||
(a = as[getProbe() & m]) == null || (c = cs[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x))) !(uncontended = c.cas(v = c.value, v + x)))
longAccumulate(x, null, uncontended); longAccumulate(x, null, uncontended);
} }
} }
@ -117,12 +117,12 @@ public class LongAdder extends Striped64 implements Serializable {
* @return the sum * @return the sum
*/ */
public long sum() { public long sum() {
Cell[] as = cells; Cell[] cs = cells;
long sum = base; long sum = base;
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
sum += a.value; sum += c.value;
} }
return sum; return sum;
} }
@ -135,12 +135,12 @@ public class LongAdder extends Striped64 implements Serializable {
* known that no threads are concurrently updating. * known that no threads are concurrently updating.
*/ */
public void reset() { public void reset() {
Cell[] as = cells; Cell[] cs = cells;
base = 0L; base = 0L;
if (as != null) { if (cs != null) {
for (Cell a : as) for (Cell c : cs)
if (a != null) if (c != null)
a.reset(); c.reset();
} }
} }
@ -155,15 +155,12 @@ public class LongAdder extends Striped64 implements Serializable {
* @return the sum * @return the sum
*/ */
public long sumThenReset() { public long sumThenReset() {
Cell[] as = cells; Cell[] cs = cells;
long sum = base; long sum = getAndSetBase(0L);
base = 0L; if (cs != null) {
if (as != null) { for (Cell c : cs) {
for (Cell a : as) { if (c != null)
if (a != null) { sum += c.getAndSet(0L);
sum += a.value;
a.reset();
}
} }
} }
return sum; return sum;

View file

@ -133,6 +133,9 @@ abstract class Striped64 extends Number {
final void reset(long identity) { final void reset(long identity) {
VALUE.setVolatile(this, identity); VALUE.setVolatile(this, identity);
} }
final long getAndSet(long val) {
return (long)VALUE.getAndSet(this, val);
}
// VarHandle mechanics // VarHandle mechanics
private static final VarHandle VALUE; private static final VarHandle VALUE;
@ -178,6 +181,10 @@ abstract class Striped64 extends Number {
return BASE.compareAndSet(this, cmp, val); return BASE.compareAndSet(this, cmp, val);
} }
final long getAndSetBase(long val) {
return (long)BASE.getAndSet(this, val);
}
/** /**
* CASes the cellsBusy field from 0 to 1 to acquire lock. * CASes the cellsBusy field from 0 to 1 to acquire lock.
*/ */
@ -228,9 +235,9 @@ abstract class Striped64 extends Number {
} }
boolean collide = false; // True if last slot nonempty boolean collide = false; // True if last slot nonempty
done: for (;;) { done: for (;;) {
Cell[] as; Cell a; int n; long v; Cell[] cs; Cell c; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) { if ((cs = cells) != null && (n = cs.length) > 0) {
if ((a = as[(n - 1) & h]) == null) { if ((c = cs[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(x); // Optimistically create Cell r = new Cell(x); // Optimistically create
if (cellsBusy == 0 && casCellsBusy()) { if (cellsBusy == 0 && casCellsBusy()) {
@ -252,17 +259,17 @@ abstract class Striped64 extends Number {
} }
else if (!wasUncontended) // CAS already known to fail else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash wasUncontended = true; // Continue after rehash
else if (a.cas(v = a.value, else if (c.cas(v = c.value,
(fn == null) ? v + x : fn.applyAsLong(v, x))) (fn == null) ? v + x : fn.applyAsLong(v, x)))
break; break;
else if (n >= NCPU || cells != as) else if (n >= NCPU || cells != cs)
collide = false; // At max size or stale collide = false; // At max size or stale
else if (!collide) else if (!collide)
collide = true; collide = true;
else if (cellsBusy == 0 && casCellsBusy()) { else if (cellsBusy == 0 && casCellsBusy()) {
try { try {
if (cells == as) // Expand table unless stale if (cells == cs) // Expand table unless stale
cells = Arrays.copyOf(as, n << 1); cells = Arrays.copyOf(cs, n << 1);
} finally { } finally {
cellsBusy = 0; cellsBusy = 0;
} }
@ -271,9 +278,9 @@ abstract class Striped64 extends Number {
} }
h = advanceProbe(h); h = advanceProbe(h);
} }
else if (cellsBusy == 0 && cells == as && casCellsBusy()) { else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
try { // Initialize table try { // Initialize table
if (cells == as) { if (cells == cs) {
Cell[] rs = new Cell[2]; Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x); rs[h & 1] = new Cell(x);
cells = rs; cells = rs;
@ -312,9 +319,9 @@ abstract class Striped64 extends Number {
} }
boolean collide = false; // True if last slot nonempty boolean collide = false; // True if last slot nonempty
done: for (;;) { done: for (;;) {
Cell[] as; Cell a; int n; long v; Cell[] cs; Cell c; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) { if ((cs = cells) != null && (n = cs.length) > 0) {
if ((a = as[(n - 1) & h]) == null) { if ((c = cs[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(Double.doubleToRawLongBits(x)); Cell r = new Cell(Double.doubleToRawLongBits(x));
if (cellsBusy == 0 && casCellsBusy()) { if (cellsBusy == 0 && casCellsBusy()) {
@ -336,16 +343,16 @@ abstract class Striped64 extends Number {
} }
else if (!wasUncontended) // CAS already known to fail else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash wasUncontended = true; // Continue after rehash
else if (a.cas(v = a.value, apply(fn, v, x))) else if (c.cas(v = c.value, apply(fn, v, x)))
break; break;
else if (n >= NCPU || cells != as) else if (n >= NCPU || cells != cs)
collide = false; // At max size or stale collide = false; // At max size or stale
else if (!collide) else if (!collide)
collide = true; collide = true;
else if (cellsBusy == 0 && casCellsBusy()) { else if (cellsBusy == 0 && casCellsBusy()) {
try { try {
if (cells == as) // Expand table unless stale if (cells == cs) // Expand table unless stale
cells = Arrays.copyOf(as, n << 1); cells = Arrays.copyOf(cs, n << 1);
} finally { } finally {
cellsBusy = 0; cellsBusy = 0;
} }
@ -354,9 +361,9 @@ abstract class Striped64 extends Number {
} }
h = advanceProbe(h); h = advanceProbe(h);
} }
else if (cellsBusy == 0 && cells == as && casCellsBusy()) { else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
try { // Initialize table try { // Initialize table
if (cells == as) { if (cells == cs) {
Cell[] rs = new Cell[2]; Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(Double.doubleToRawLongBits(x)); rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
cells = rs; cells = rs;