RFR: 8290079: Reduce interaction with volatile in static initializer of BigInteger
Сергей Цыпанов
duke at openjdk.org
Tue Jul 12 21:31:46 UTC 2022
On Tue, 12 Jul 2022 09:18:19 GMT, Raffaello Giulietti <duke at openjdk.org> wrote:
>> `BigInteger.powerCache` is volatile and should be assigned only once in static initializer.
>
> Usually yes, but since a static initializer is executed by at most one thread by using a locking protocol before any other static code is ever executed, the runtime _could_ (but I'm not sure it it really does) treat the volatile in the for loop as a local.
> But I would approve your change because it makes this more explicit.
@rgiulietti I've copy-pasted class-loading benchmark from JMH samples
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 5)
@Fork(value = 5, jvmArgsAppend = {"-Xms1g", "-Xmx1g"})
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class Classy {
@Benchmark
public Class<?> load() throws ClassNotFoundException {
return Class.forName("com.tsypanov.slp.Sample", true, new XLoader());
}
public static class XLoader extends URLClassLoader {
private static final byte[] X_BYTECODE = new byte[]{ /*..*/};
public XLoader() {
super(new URL[0], ClassLoader.getSystemClassLoader());
}
@Override
protected Class<?> findClass(final String name) {
return defineClass(name, X_BYTECODE, 0, X_BYTECODE.length);
}
}
}
and used it to measure loading a class from `byte[]` copied from the following class compiled:
class Sample {
static volatile int[] items = new int[100];
static {
for (int i = 0; i < items.length; i++) {
items[i] = ThreadLocalRandom.current().nextInt();
}
}
}
I ran the benchmark with `java -jar target/sleep-benchmarks.jar Classy -prof cl` and for the version above got these results (Java 17):
Benchmark Mode Cnt Score Error Units
Classy.load avgt 50 96247.202 ± 548.137 ns/op
Classy.load:·class.load avgt 50 259912.089 ± 1485.314 classes/sec
Classy.load:·class.load.norm avgt 50 1.000 ± 0.001 classes/op
Classy.load:·class.unload avgt 50 260243.318 ± 3673.515 classes/sec
Classy.load:·class.unload.norm avgt 50 1.001 ± 0.014 classes/op
Then I've modified the class in the same way I did in this PR:
class Sample {
static volatile int[] items;
static {
int[] items = new int[100];
for (int i = 0; i < items.length; i++) {
items[i] = ThreadLocalRandom.current().nextInt();
}
Sample.items = items;
}
}
and for modified code got
Benchmark Mode Cnt Score Error Units
Classy.load avgt 50 63955.673 ± 147.470 ns/op
Classy.load:·class.load avgt 50 391101.854 ± 925.013 classes/sec
Classy.load:·class.load.norm avgt 50 1.000 ± 0.001 classes/op
Classy.load:·class.unload avgt 50 390800.851 ± 2307.589 classes/sec
Classy.load:·class.unload.norm avgt 50 0.999 ± 0.006 classes/op
>From this I conclude that volatile costs are still there no matter whether we deal with static or non-static initializers.
-------------
PR: https://git.openjdk.org/jdk/pull/9451
More information about the core-libs-dev
mailing list