Skip to content

Commit 9dd93c6

Browse files
author
Andrew Haley
committed
8361497: Scoped Values: orElse and orElseThrow do not access the cache
Reviewed-by: alanb
1 parent f8c8bcf commit 9dd93c6

File tree

2 files changed

+100
-15
lines changed

2 files changed

+100
-15
lines changed

src/java.base/share/classes/java/lang/ScopedValue.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ public T get() {
572572

573573
@SuppressWarnings("unchecked")
574574
private T slowGet() {
575-
var value = findBinding();
575+
Object value = scopedValueBindings().find(this);
576576
if (value == Snapshot.NIL) {
577577
throw new NoSuchElementException("ScopedValue not bound");
578578
}
@@ -581,32 +581,35 @@ private T slowGet() {
581581
}
582582

583583
/**
584-
* {@return {@code true} if this scoped value is bound in the current thread}
584+
* Return the value of the scoped value or NIL if not bound.
585+
* Consult the cache, and only if the value is not found there
586+
* search the list of bindings. Update the cache if the binding
587+
* was found.
585588
*/
586-
public boolean isBound() {
589+
private Object findBinding() {
587590
Object[] objects = scopedValueCache();
588591
if (objects != null) {
589592
int n = (hash & Cache.Constants.SLOT_MASK) * 2;
590593
if (objects[n] == this) {
591-
return true;
594+
return objects[n + 1];
592595
}
593596
n = ((hash >>> Cache.INDEX_BITS) & Cache.Constants.SLOT_MASK) * 2;
594597
if (objects[n] == this) {
595-
return true;
598+
return objects[n + 1];
596599
}
597600
}
598-
var value = findBinding();
599-
boolean result = (value != Snapshot.NIL);
600-
if (result) Cache.put(this, value);
601-
return result;
601+
Object value = scopedValueBindings().find(this);
602+
boolean found = (value != Snapshot.NIL);
603+
if (found) Cache.put(this, value);
604+
return value;
602605
}
603606

604607
/**
605-
* Return the value of the scoped value or NIL if not bound.
608+
* {@return {@code true} if this scoped value is bound in the current thread}
606609
*/
607-
private Object findBinding() {
608-
Object value = scopedValueBindings().find(this);
609-
return value;
610+
public boolean isBound() {
611+
Object obj = findBinding();
612+
return obj != Snapshot.NIL;
610613
}
611614

612615
/**

test/micro/org/openjdk/bench/java/lang/ScopedValues.java

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.openjdk.jmh.annotations.*;
3030
import org.openjdk.jmh.infra.Blackhole;
3131

32+
import static java.lang.ScopedValue.where;
3233
import static org.openjdk.bench.java.lang.ScopedValuesData.*;
3334

3435
/**
@@ -102,6 +103,26 @@ public int thousandMaybeGets(Blackhole bh) throws Exception {
102103
return result;
103104
}
104105

106+
@Benchmark
107+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
108+
public int thousandUnboundOrElses(Blackhole bh) throws Exception {
109+
int result = 0;
110+
for (int i = 0; i < 1_000; i++) {
111+
result += ScopedValuesData.unbound.orElse(1);
112+
}
113+
return result;
114+
}
115+
116+
@Benchmark
117+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
118+
public int thousandBoundOrElses(Blackhole bh) throws Exception {
119+
int result = 0;
120+
for (int i = 0; i < 1_000; i++) {
121+
result += ScopedValuesData.sl1.orElse(1);
122+
}
123+
return result;
124+
}
125+
105126
// Test 2: stress the ScopedValue cache.
106127
// The idea here is to use a bunch of bound values cyclically, which
107128
// stresses the ScopedValue cache.
@@ -137,12 +158,12 @@ public int sixValues_ThreadLocal() throws Exception {
137158
@Benchmark
138159
@OutputTimeUnit(TimeUnit.NANOSECONDS)
139160
public int CreateBindThenGetThenRemove_ScopedValue() throws Exception {
140-
return ScopedValue.where(sl1, THE_ANSWER).call(sl1::get);
161+
return where(sl1, THE_ANSWER).call(sl1::get);
141162
}
142163

143164

144165
// Create a Carrier ahead of time: might be slightly faster
145-
private static final ScopedValue.Carrier HOLD_42 = ScopedValue.where(sl1, 42);
166+
private static final ScopedValue.Carrier HOLD_42 = where(sl1, 42);
146167
@Benchmark
147168
@OutputTimeUnit(TimeUnit.NANOSECONDS)
148169
public int bindThenGetThenRemove_ScopedValue() throws Exception {
@@ -230,4 +251,65 @@ public Object newInstance() {
230251
ScopedValue<Integer> val = ScopedValue.newInstance();
231252
return val;
232253
}
254+
255+
// Test 6: Performance with a large number of bindings
256+
static final long deepCall(ScopedValue<Integer> outer, long n) {
257+
long result = 0;
258+
if (n > 0) {
259+
ScopedValue<Long> sv = ScopedValue.newInstance();
260+
return where(sv, n).call(() -> deepCall(outer, n - 1));
261+
} else {
262+
for (int i = 0; i < 1_000_000; i++) {
263+
result += outer.orElse(12);
264+
}
265+
}
266+
return result;
267+
}
268+
269+
@Benchmark
270+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
271+
public long deepBindingTest1() {
272+
return deepCall(ScopedValuesData.unbound, 1000);
273+
}
274+
275+
@Benchmark
276+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
277+
public long deepBindingTest2() {
278+
return deepCall(ScopedValuesData.sl1, 1000);
279+
}
280+
281+
282+
// Test 7: Performance with a large number of bindings
283+
// Different from Test 6 in that we recursively build a very long
284+
// list of Carriers and invoke Carrier.call() only once.
285+
static final long deepCall2(ScopedValue<Integer> outer, ScopedValue.Carrier carrier, long n) {
286+
long result = 0;
287+
if (n > 0) {
288+
ScopedValue<Long> sv = ScopedValue.newInstance();
289+
return deepCall2(outer, carrier.where(sv, n), n - 1);
290+
} else {
291+
result = carrier.call(() -> {
292+
long sum = 0;
293+
for (int i = 0; i < 1_000_000; i++) {
294+
sum += outer.orElse(12);
295+
}
296+
return sum;
297+
});
298+
}
299+
return result;
300+
}
301+
302+
@Benchmark
303+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
304+
public long deepBindingTest3() {
305+
return deepCall2(ScopedValuesData.unbound, where(ScopedValuesData.sl2,0), 1000);
306+
}
307+
308+
@Benchmark
309+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
310+
public long deepBindingTest4() {
311+
return deepCall2(ScopedValuesData.sl1, where(ScopedValuesData.sl2, 0), 1000);
312+
}
313+
314+
233315
}

0 commit comments

Comments
 (0)