undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
123
test/micro/org/openjdk/bench/java/util/concurrent/Atomic.java
Normal file
123
test/micro/org/openjdk/bench/java/util/concurrent/Atomic.java
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OperationsPerInvocation;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public class Atomic {
|
||||
|
||||
public AtomicInteger aInteger;
|
||||
public AtomicLong aLong;
|
||||
public AtomicBoolean aBool;
|
||||
|
||||
public Object testObject1;
|
||||
public Object testObject2;
|
||||
public AtomicReference<Object> aReference;
|
||||
|
||||
/**
|
||||
* The test variables are allocated every iteration so you can assume they are initialized to get similar behaviour
|
||||
* across iterations
|
||||
*/
|
||||
@Setup(Level.Iteration)
|
||||
public void setupIteration() {
|
||||
testObject1 = new Object();
|
||||
testObject2 = new Object();
|
||||
aInteger = new AtomicInteger(0);
|
||||
aBool = new AtomicBoolean(false);
|
||||
aReference = new AtomicReference<>(testObject1);
|
||||
aLong = new AtomicLong(0);
|
||||
}
|
||||
|
||||
|
||||
/** Always swap in value. This test should be compiled into a CAS */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(2)
|
||||
public void testAtomicIntegerAlways(Blackhole bh) {
|
||||
bh.consume(aInteger.compareAndSet(0, 2));
|
||||
bh.consume(aInteger.compareAndSet(2, 0));
|
||||
}
|
||||
|
||||
/** Never write a value just return the old one. This test should be compiled into a CAS */
|
||||
@Benchmark
|
||||
public void testAtomicIntegerNever(Blackhole bh) {
|
||||
bh.consume(aInteger.compareAndSet(1, 3));
|
||||
}
|
||||
|
||||
/** Flips an atomic boolean on and off */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(2)
|
||||
public void testAtomicBooleanFlip(Blackhole bh) {
|
||||
bh.consume(aBool.getAndSet(true));
|
||||
bh.consume(aBool.getAndSet(false));
|
||||
}
|
||||
|
||||
/** Writes same value over and over */
|
||||
@Benchmark
|
||||
public void testAtomicBooleanSame(Blackhole bh) {
|
||||
bh.consume(aBool.getAndSet(true));
|
||||
}
|
||||
|
||||
/** Increment and get over multiple threads */
|
||||
@Benchmark
|
||||
public void testAtomicIntegerGetAndIncrement(Blackhole bh) {
|
||||
bh.consume(aInteger.getAndIncrement());
|
||||
}
|
||||
|
||||
/** Increment and get over multiple threads */
|
||||
@Benchmark
|
||||
public void testAtomicLongGetAndIncrement(Blackhole bh) {
|
||||
bh.consume(aLong.getAndIncrement());
|
||||
}
|
||||
|
||||
/** Swap a few references */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(2)
|
||||
public void testAtomicReference(Blackhole bh) {
|
||||
bh.consume(aReference.compareAndSet(testObject1, testObject2));
|
||||
bh.consume(aReference.compareAndSet(testObject2, testObject1));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
/**
|
||||
* Benchmarks assesses the performance of new Atomic* API.
|
||||
*
|
||||
* Implementation notes:
|
||||
* - atomic instances are padded to eliminate false sharing
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Thread)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public class AtomicIntegerUpdateAndGet {
|
||||
|
||||
private PaddedAtomicInteger count;
|
||||
private int value = 42;
|
||||
private IntUnaryOperator captureOp;
|
||||
private IntUnaryOperator noCaptureOp;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
count = new PaddedAtomicInteger();
|
||||
noCaptureOp = new IntUnaryOperator() {
|
||||
public int applyAsInt(int v) {
|
||||
return v + 42;
|
||||
}
|
||||
};
|
||||
captureOp = new IntUnaryOperator() {
|
||||
public int applyAsInt(int v) {
|
||||
return v + value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testAddAndGet() {
|
||||
return count.addAndGet(42);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testInnerNoCapture() {
|
||||
return count.updateAndGet(new IntUnaryOperator() {
|
||||
public int applyAsInt(int v) {
|
||||
return v + 42;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testInnerCapture() {
|
||||
return count.updateAndGet(new IntUnaryOperator() {
|
||||
public int applyAsInt(int v) {
|
||||
return v + value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testInnerCaptureCached() {
|
||||
return count.updateAndGet(captureOp);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testInnerNoCaptureCached() {
|
||||
return count.updateAndGet(noCaptureOp);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testLambdaNoCapture() {
|
||||
return count.updateAndGet(x -> x + 42);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testLambdaCapture() {
|
||||
return count.updateAndGet(x -> x + value);
|
||||
}
|
||||
|
||||
private static class PaddedAtomicInteger extends AtomicInteger {
|
||||
private volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06, pad07;
|
||||
private volatile long pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@State(Scope.Benchmark)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Fork(1)
|
||||
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||
public class CopyOnWriteArrayListBenchmark {
|
||||
|
||||
private static byte[] getSerializedBytes(CopyOnWriteArrayList<?> list) throws IOException {
|
||||
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
||||
ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut);
|
||||
objectOut.writeObject(list);
|
||||
|
||||
objectOut.close();
|
||||
return bytesOut.toByteArray();
|
||||
}
|
||||
|
||||
private Collection<Object> emptyCollection = new ArrayList<>();
|
||||
private Object[] emptyArray = new Object[0];
|
||||
|
||||
private Collection<Object> oneItemCollection = Arrays.asList("");
|
||||
private Object[] oneItemArray = new Object[] { "" };
|
||||
|
||||
private CopyOnWriteArrayList<?> emptyInstance = new CopyOnWriteArrayList<>();
|
||||
private CopyOnWriteArrayList<?> oneItemInstance = new CopyOnWriteArrayList<>(oneItemArray);
|
||||
|
||||
private byte[] emptyInstanceBytes;
|
||||
private byte[] oneInstanceBytes;
|
||||
|
||||
public CopyOnWriteArrayListBenchmark() {
|
||||
try {
|
||||
emptyInstanceBytes = getSerializedBytes(emptyInstance);
|
||||
oneInstanceBytes = getSerializedBytes(oneItemInstance);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void clear() {
|
||||
// have to create a new instance on each execution
|
||||
((CopyOnWriteArrayList<?>) oneItemInstance.clone()).clear();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void clearEmpty() {
|
||||
emptyInstance.clear();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> createInstanceArray() {
|
||||
return new CopyOnWriteArrayList<>(oneItemArray);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> createInstanceArrayEmpty() {
|
||||
return new CopyOnWriteArrayList<>(emptyArray);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> createInstanceCollection() {
|
||||
return new CopyOnWriteArrayList<>(oneItemCollection);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> createInstanceCollectionEmpty() {
|
||||
return new CopyOnWriteArrayList<>(emptyCollection);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> createInstanceDefault() {
|
||||
return new CopyOnWriteArrayList<Object>();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> readInstance() throws IOException, ClassNotFoundException {
|
||||
try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(oneInstanceBytes))) {
|
||||
return (CopyOnWriteArrayList<?>) objIn.readObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> readInstanceEmpty() throws IOException, ClassNotFoundException {
|
||||
try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(emptyInstanceBytes))) {
|
||||
return (CopyOnWriteArrayList<?>) objIn.readObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> removeObjectLastRemaining() {
|
||||
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
|
||||
list.add("");
|
||||
list.remove("");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> removeIndexLastRemaining() {
|
||||
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
|
||||
list.add("");
|
||||
list.remove(0);
|
||||
return list;
|
||||
}
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> removeObject() {
|
||||
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
|
||||
list.add("");
|
||||
list.add("a");
|
||||
list.remove("");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public CopyOnWriteArrayList<?> remove() {
|
||||
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
|
||||
list.add("");
|
||||
list.add("a");
|
||||
list.remove(0);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Supplier;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
|
||||
/**
|
||||
* Benchmark to compare delayed task scheduling with ScheduledThreadPoolExcutor and ForkJoinPool.
|
||||
*/
|
||||
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
@Fork(value = 3)
|
||||
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
|
||||
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
|
||||
@State(Scope.Thread)
|
||||
public class DelayedTasks {
|
||||
|
||||
private Supplier<ScheduledExecutorService> stpeSupplier;
|
||||
private Supplier<ScheduledExecutorService> fjpSupplier;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
stpeSupplier = () -> {
|
||||
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
|
||||
((ScheduledThreadPoolExecutor) executor).setRemoveOnCancelPolicy(true);
|
||||
return executor;
|
||||
};
|
||||
int nprocs = Runtime.getRuntime().availableProcessors();
|
||||
fjpSupplier = () -> new ForkJoinPool(nprocs);
|
||||
}
|
||||
|
||||
@Param({"100", "1000", "10000"})
|
||||
int delayedTasks;
|
||||
|
||||
// delayed tasks cancelled by main thread
|
||||
private void mainThreadCancels(Supplier<ScheduledExecutorService> supplier) {
|
||||
try (ScheduledExecutorService ses = supplier.get()) {
|
||||
var futures = new ScheduledFuture[delayedTasks];
|
||||
for (int i = 0; i < delayedTasks; i++) {
|
||||
futures[i] = ses.schedule(() -> { }, 30L, TimeUnit.MINUTES);
|
||||
}
|
||||
for (ScheduledFuture<?> f : futures) {
|
||||
f.cancel(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delayed tasks cancelled by virtual threads
|
||||
private void virtualThreadCancels(Supplier<ScheduledExecutorService> supplier) throws Exception {
|
||||
try (ScheduledExecutorService ses = supplier.get()) {
|
||||
var futures = new ScheduledFuture[delayedTasks];
|
||||
var threads = new Thread[delayedTasks];
|
||||
for (int i = 0; i < delayedTasks; i++) {
|
||||
ScheduledFuture<?> future = ses.schedule(() -> { }, 30L, TimeUnit.MINUTES);
|
||||
futures[i] = future;
|
||||
threads[i] = Thread.ofVirtual().start(() -> future.cancel(false));
|
||||
}
|
||||
for (Thread t : threads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delayed task executes
|
||||
private void delayedTaskExecutes(Supplier<ScheduledExecutorService> supplier) throws Exception {
|
||||
try (ScheduledExecutorService ses = supplier.get()) {
|
||||
var futures = new ScheduledFuture[delayedTasks];
|
||||
for (int i = 0; i < delayedTasks; i++) {
|
||||
futures[i] = ses.schedule(() -> { }, 10L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
for (ScheduledFuture<?> f : futures) {
|
||||
f.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void spteMainThreadCancels() {
|
||||
mainThreadCancels(stpeSupplier);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void spteVirtualThreadCancels() throws Exception {
|
||||
virtualThreadCancels(stpeSupplier);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void spteDelayedTaskExecutes() throws Exception {
|
||||
delayedTaskExecutes(stpeSupplier);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void fjpMainThreadCancels() {
|
||||
mainThreadCancels(fjpSupplier);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void fjpVirtualThreadCancels() throws Exception {
|
||||
virtualThreadCancels(fjpSupplier);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void fjpDelayedTaskExecutes() throws Exception {
|
||||
delayedTaskExecutes(fjpSupplier);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Benchmark assesses ForkJoinPool forking infrastructure.
|
||||
*
|
||||
* @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
|
||||
*/
|
||||
@OutputTimeUnit(TimeUnit.MINUTES)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 2)
|
||||
@Measurement(iterations = 5, time = 2)
|
||||
@Fork(3)
|
||||
public class ForkJoinPoolForking {
|
||||
|
||||
/**
|
||||
* Implementation notes:
|
||||
*
|
||||
* This test harnesses forking infrastructure within FJP.
|
||||
* As such, no slack is given for allocating any humble number of tasks: the goal is to fork a lot.
|
||||
* The approximate number of tasks is (SIZE / THRESHOLD).
|
||||
*
|
||||
* Raw baseline gives the idea for compute bound for this benchmark.
|
||||
* FJP could be faster than baseline, because the baseline is single-threaded.
|
||||
*/
|
||||
|
||||
@Param("10000000")
|
||||
private int size;
|
||||
|
||||
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
|
||||
@State(Scope.Benchmark)
|
||||
public static class TestState {
|
||||
|
||||
@Param("0")
|
||||
private int workers;
|
||||
|
||||
@Param({"1", "2", "3", "4", "5", "6", "7", "8"})
|
||||
private int threshold;
|
||||
|
||||
private ForkJoinPool fjpSync;
|
||||
private ForkJoinPool fjpAsync;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
if (workers == 0) {
|
||||
workers = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
fjpSync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false);
|
||||
fjpAsync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
|
||||
}
|
||||
|
||||
|
||||
@TearDown
|
||||
public void teardown() {
|
||||
fjpSync.shutdownNow();
|
||||
fjpAsync.shutdownNow();
|
||||
}
|
||||
}
|
||||
private Problem problem;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
problem = new Problem(size);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long testExplicit_Sync(TestState state) throws ExecutionException, InterruptedException {
|
||||
return state.fjpSync.invoke(new ExplicitTask(problem, 0, problem.size(), state.threshold));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long testExplicit_Async(TestState state) throws ExecutionException, InterruptedException {
|
||||
return state.fjpAsync.invoke(new ExplicitTask(problem, 0, problem.size(), state.threshold));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long testStandard_Sync(TestState state) throws ExecutionException, InterruptedException {
|
||||
return state.fjpSync.invoke(new StandardTask(problem, 0, problem.size(), state.threshold));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long testStandard_Async(TestState state) throws ExecutionException, InterruptedException {
|
||||
return state.fjpAsync.invoke(new StandardTask(problem, 0, problem.size(), state.threshold));
|
||||
}
|
||||
|
||||
private static class ExplicitTask extends RecursiveTask<Long> {
|
||||
private final Problem problem;
|
||||
private final int l;
|
||||
private final int r;
|
||||
private final int thresh;
|
||||
|
||||
public ExplicitTask(Problem p, int l, int r, int thresh) {
|
||||
this.problem = p;
|
||||
this.l = l;
|
||||
this.r = r;
|
||||
this.thresh = thresh;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long compute() {
|
||||
if (r - l <= thresh) {
|
||||
return problem.solve(l, r);
|
||||
}
|
||||
|
||||
int mid = (l + r) >>> 1;
|
||||
ForkJoinTask<Long> t1 = new ExplicitTask(problem, l, mid, thresh);
|
||||
ForkJoinTask<Long> t2 = new ExplicitTask(problem, mid, r, thresh);
|
||||
|
||||
t1.fork();
|
||||
t2.fork();
|
||||
|
||||
long res = 0;
|
||||
res += t2.join();
|
||||
res += t1.join();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StandardTask extends RecursiveTask<Long> {
|
||||
private final Problem problem;
|
||||
private final int l;
|
||||
private final int r;
|
||||
private final int thresh;
|
||||
|
||||
public StandardTask(Problem p, int l, int r, int thresh) {
|
||||
this.problem = p;
|
||||
this.l = l;
|
||||
this.r = r;
|
||||
this.thresh = thresh;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long compute() {
|
||||
if (r - l <= thresh) {
|
||||
return problem.solve(l, r);
|
||||
}
|
||||
|
||||
int mid = (l + r) >>> 1;
|
||||
ForkJoinTask<Long> t1 = new StandardTask(problem, l, mid, thresh);
|
||||
ForkJoinTask<Long> t2 = new StandardTask(problem, mid, r, thresh);
|
||||
|
||||
ForkJoinTask.invokeAll(t1, t2);
|
||||
long res = 0;
|
||||
res += t1.join();
|
||||
res += t2.join();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Benchmark assesses general ForkJoinPool performance with simple tasks
|
||||
*
|
||||
* @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
|
||||
*/
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 2)
|
||||
@Measurement(iterations = 5, time = 2)
|
||||
@Fork(3)
|
||||
public class ForkJoinPoolRawCallable {
|
||||
|
||||
/**
|
||||
* Implementation notes:
|
||||
*
|
||||
* This test submits empty callables.
|
||||
* Callables are submitted in batches, to prevent convoying by driver threads.
|
||||
* One driver thread can saturate up to BATCH_SIZE threads.
|
||||
*
|
||||
* One baseline includes raw throughput, without submissions to executors.
|
||||
* This is not considered as fair comparison, but left around as basic compute baseline.
|
||||
* Executors could not possibly be faster than that.
|
||||
*
|
||||
* Another baseline includes ThreadPoolExecutor.
|
||||
* Note that this baseline is inherently non-scalable with ABQ backing TPE.
|
||||
* The size of ABQ is chosen to accommodate tons of threads, which can also suffer due to cache effects.
|
||||
*
|
||||
* Tasks are reading public volatile field to break opportunistic optimizations in loops.
|
||||
* Tasks are pre-allocated to negate instantiation costs.
|
||||
*/
|
||||
|
||||
@Param("0")
|
||||
private int workers;
|
||||
|
||||
@Param("1000")
|
||||
private int batchSize;
|
||||
|
||||
private ThreadPoolExecutor tpe;
|
||||
private ForkJoinPool fjpSync;
|
||||
private ForkJoinPool fjpAsync;
|
||||
private List<SampleTask> tasks;
|
||||
|
||||
public volatile int arg = 42;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
SampleTask task = new SampleTask();
|
||||
|
||||
tasks = new ArrayList<>();
|
||||
for (int c = 0; c < batchSize; c++) {
|
||||
tasks.add(task);
|
||||
}
|
||||
|
||||
if (workers == 0) {
|
||||
workers = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
tpe = new ThreadPoolExecutor(workers, workers, 1, TimeUnit.HOURS, new ArrayBlockingQueue<>(batchSize * batchSize));
|
||||
fjpSync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false);
|
||||
fjpAsync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void teardown() {
|
||||
tpe.shutdownNow();
|
||||
fjpSync.shutdownNow();
|
||||
fjpAsync.shutdownNow();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int baseline_raw() throws Exception {
|
||||
int s = 0;
|
||||
for (SampleTask t : tasks) {
|
||||
s += t.call();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int baseline_TPE() throws Exception {
|
||||
return doWork(tpe);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testSync() throws ExecutionException, InterruptedException {
|
||||
return doWork(fjpSync);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testAsync() throws ExecutionException, InterruptedException {
|
||||
return doWork(fjpAsync);
|
||||
}
|
||||
|
||||
public int doWork(ExecutorService service) throws ExecutionException, InterruptedException {
|
||||
List<Future<Integer>> futures = new ArrayList<>(tasks.size());
|
||||
for (SampleTask task : tasks) {
|
||||
futures.add(service.submit(task));
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for (Future<Integer> future : futures) {
|
||||
s += future.get();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public class SampleTask implements Callable<Integer> {
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Benchmark assesses ForkJoinPool performance with dependence on threshold.
|
||||
*/
|
||||
@OutputTimeUnit(TimeUnit.MINUTES)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 2)
|
||||
@Measurement(iterations = 5, time = 2)
|
||||
@Fork(3)
|
||||
public class ForkJoinPoolThresholdAutoQueued {
|
||||
|
||||
/**
|
||||
* Implementation notes:
|
||||
*
|
||||
* This test solves the problem with threshold = 1, and adaptive heuristics. The optimal level is static,
|
||||
* and lies somewhere in 1..2 interval. Note the test degrades significantly when heuristic starts to fail,
|
||||
* and the throughput is buried under FJP overheads.
|
||||
*
|
||||
* Baseline includes solving problem sequentially. Hence, each test provides the speedup for parallel execution
|
||||
* versus sequential version.
|
||||
*/
|
||||
|
||||
@Param("10000000")
|
||||
private int size;
|
||||
|
||||
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
|
||||
@State(Scope.Benchmark)
|
||||
public static class TestState {
|
||||
|
||||
@Param("0")
|
||||
private int workers;
|
||||
|
||||
@Param({"1", "2", "3", "4", "5", "6", "7", "8"})
|
||||
private int threshold;
|
||||
|
||||
private ForkJoinPool fjp;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
if (workers == 0) {
|
||||
workers = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
fjp = new ForkJoinPool(workers);
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void teardown() {
|
||||
fjp.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private Problem problem;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
problem = new Problem(size);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long baselineRaw() {
|
||||
return problem.solve();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long test(TestState state) throws ExecutionException, InterruptedException {
|
||||
return state.fjp.invoke(new AutoQueuedTask(state.threshold, problem, 0, problem.size()));
|
||||
}
|
||||
|
||||
private static class AutoQueuedTask extends RecursiveTask<Long> {
|
||||
private final int thr;
|
||||
private final Problem problem;
|
||||
private final int l;
|
||||
private final int r;
|
||||
|
||||
public AutoQueuedTask(int thr, Problem p, int l, int r) {
|
||||
this.thr = thr;
|
||||
this.problem = p;
|
||||
this.l = l;
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long compute() {
|
||||
if (r - l <= 1 || getQueuedTaskCount() >= thr) {
|
||||
return problem.solve(l, r);
|
||||
}
|
||||
|
||||
int mid = (l + r) >>> 1;
|
||||
ForkJoinTask<Long> t1 = new AutoQueuedTask(thr, problem, l, mid);
|
||||
ForkJoinTask<Long> t2 = new AutoQueuedTask(thr, problem, mid, r);
|
||||
|
||||
t2.fork();
|
||||
|
||||
long res = 0;
|
||||
res += t1.invoke();
|
||||
res += t2.join();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Benchmark assesses ForkJoinPool performance with dependence on threshold.
|
||||
*/
|
||||
@OutputTimeUnit(TimeUnit.MINUTES)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 2)
|
||||
@Measurement(iterations = 5, time = 2)
|
||||
@Fork(3)
|
||||
public class ForkJoinPoolThresholdAutoSurplus {
|
||||
|
||||
/**
|
||||
* Implementation notes:
|
||||
*
|
||||
* This test solves the problem with threshold = 1, and adaptive heuristics. The optimal level is static,
|
||||
* and lies somewhere in 1..2 interval. Note the test degrades significantly when heuristic starts to fail,
|
||||
* and the throughput is buried under FJP overheads.
|
||||
*
|
||||
* Baseline includes solving problem sequentially. Hence, each test provides the speedup for parallel execution
|
||||
* versus sequential version.
|
||||
*/
|
||||
|
||||
@Param("10000000")
|
||||
private int size;
|
||||
|
||||
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
|
||||
@State(Scope.Benchmark)
|
||||
public static class TestState {
|
||||
|
||||
@Param("0")
|
||||
private int workers;
|
||||
|
||||
@Param({"1", "2", "3", "4", "5", "6", "7", "8"})
|
||||
private int threshold;
|
||||
|
||||
private ForkJoinPool fjp;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
if (workers == 0) {
|
||||
workers = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
fjp = new ForkJoinPool(workers);
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void teardown() {
|
||||
fjp.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private Problem problem;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
problem = new Problem(size);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long baselineRaw() {
|
||||
return problem.solve();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long test(TestState state) throws ExecutionException, InterruptedException {
|
||||
return state.fjp.invoke(new AutoQueuedTask(state.threshold, problem, 0, problem.size()));
|
||||
}
|
||||
|
||||
private static class AutoQueuedTask extends RecursiveTask<Long> {
|
||||
private final int thr;
|
||||
private final Problem problem;
|
||||
private final int l;
|
||||
private final int r;
|
||||
|
||||
public AutoQueuedTask(int thr, Problem p, int l, int r) {
|
||||
this.thr = thr;
|
||||
this.problem = p;
|
||||
this.l = l;
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long compute() {
|
||||
if (r - l <= 1 || getSurplusQueuedTaskCount() >= thr) {
|
||||
return problem.solve(l, r);
|
||||
}
|
||||
|
||||
int mid = (l + r) >>> 1;
|
||||
ForkJoinTask<Long> t1 = new AutoQueuedTask(thr, problem, l, mid);
|
||||
ForkJoinTask<Long> t2 = new AutoQueuedTask(thr, problem, mid, r);
|
||||
|
||||
t2.fork();
|
||||
|
||||
long res = 0;
|
||||
res += t1.invoke();
|
||||
res += t2.join();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Benchmark assesses ForkJoinPool performance with dependence on threshold.
|
||||
*/
|
||||
@OutputTimeUnit(TimeUnit.MINUTES)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 2)
|
||||
@Measurement(iterations = 5, time = 2)
|
||||
@Fork(3)
|
||||
public class ForkJoinPoolThresholdStatic {
|
||||
|
||||
/**
|
||||
* Implementation notes:
|
||||
*
|
||||
* This test solves the problem on different threshold levels.
|
||||
* The optimal level depends on available parallelism.
|
||||
* Lower thresholds will suffer because of ForkJoinPool infrastructure overheads.
|
||||
* Higher thresholds will suffer because of lower available task parallelism.
|
||||
*
|
||||
* Baseline includes solving problem sequentially.
|
||||
* Hence, each test provides the speedup for parallel execution
|
||||
* versus sequential version.
|
||||
*/
|
||||
|
||||
@Param("10000000")
|
||||
private int size;
|
||||
|
||||
/** Encapsulate all the state depended on only by actual test. This avoids running baselines for every parameter value. */
|
||||
@State(Scope.Benchmark)
|
||||
public static class TestState {
|
||||
|
||||
@Param("0")
|
||||
private int workers;
|
||||
|
||||
@Param({"1", "5", "10", "100", "1000", "10000", "100000", "1000000", "10000000"})
|
||||
private int threshold;
|
||||
|
||||
private ForkJoinPool fjp;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
if (workers == 0) {
|
||||
workers = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
fjp = new ForkJoinPool(workers);
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void teardown() {
|
||||
fjp.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private Problem problem;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
problem = new Problem(size);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long baselineRaw() {
|
||||
return problem.solve();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Long test(TestState state) throws ExecutionException, InterruptedException {
|
||||
return state.fjp.invoke(new AdjustableThreshTask(state.threshold, problem, 0, problem.size()));
|
||||
}
|
||||
|
||||
private static class AdjustableThreshTask extends RecursiveTask<Long> {
|
||||
private final int thr;
|
||||
private final Problem problem;
|
||||
private final int l;
|
||||
private final int r;
|
||||
|
||||
public AdjustableThreshTask(int thr, Problem p, int l, int r) {
|
||||
this.thr = thr;
|
||||
this.problem = p;
|
||||
this.l = l;
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long compute() {
|
||||
if (r - l <= thr) {
|
||||
return problem.solve(l, r);
|
||||
}
|
||||
|
||||
int mid = (l + r) >>> 1;
|
||||
ForkJoinTask<Long> t1 = new AdjustableThreshTask(thr, problem, l, mid);
|
||||
ForkJoinTask<Long> t2 = new AdjustableThreshTask(thr, problem, mid, r);
|
||||
|
||||
ForkJoinTask.invokeAll(t1, t2);
|
||||
|
||||
long res = 0;
|
||||
res += t1.join();
|
||||
res += t2.join();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
238
test/micro/org/openjdk/bench/java/util/concurrent/Locks.java
Normal file
238
test/micro/org/openjdk/bench/java/util/concurrent/Locks.java
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public class Locks {
|
||||
|
||||
private ReentrantLock reentrantLock;
|
||||
private ReentrantLock fairReentrantLock;
|
||||
private ReentrantReadWriteLock reentrantRWLock;
|
||||
private ReentrantReadWriteLock fairReentrantRWLock;
|
||||
private Semaphore semaphore;
|
||||
private Semaphore fairSemaphore;
|
||||
private Lock reentrantWriteLock;
|
||||
private Mutex mutex;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
reentrantLock = new ReentrantLock(false);
|
||||
fairReentrantLock = new ReentrantLock(true);
|
||||
reentrantRWLock = new ReentrantReadWriteLock(false);
|
||||
fairReentrantRWLock = new ReentrantReadWriteLock(true);
|
||||
semaphore = new Semaphore(1, false);
|
||||
fairSemaphore = new Semaphore(1, true);
|
||||
reentrantWriteLock = new ReentrantReadWriteLock(false).writeLock();
|
||||
mutex = new Mutex();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testSynchronizedBlock() {
|
||||
synchronized (this) {
|
||||
Blackhole.consumeCPU(10);
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testFairReentrantLock() {
|
||||
fairReentrantLock.lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
fairReentrantLock.unlock();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testReentrantLock() {
|
||||
reentrantLock.lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
reentrantLock.unlock();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testFairReentrantReadWriteLock() {
|
||||
fairReentrantRWLock.readLock().lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
fairReentrantRWLock.readLock().unlock();
|
||||
}
|
||||
fairReentrantRWLock.writeLock().lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
fairReentrantRWLock.writeLock().unlock();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testReentrantReadWriteLock() {
|
||||
reentrantRWLock.readLock().lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
reentrantRWLock.readLock().unlock();
|
||||
}
|
||||
reentrantRWLock.writeLock().lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
reentrantRWLock.writeLock().unlock();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testReentrantWriteLock() {
|
||||
reentrantWriteLock.lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
reentrantWriteLock.unlock();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testFairSemaphore() throws InterruptedException {
|
||||
fairSemaphore.acquire();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
fairSemaphore.release();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testSemaphore() throws InterruptedException {
|
||||
semaphore.acquire();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
semaphore.release();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testAbstractQueueSynchronizer() {
|
||||
mutex.lock();
|
||||
try {
|
||||
Blackhole.consumeCPU(10);
|
||||
} finally {
|
||||
mutex.unlock();
|
||||
}
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private final class Mutex extends AbstractQueuedSynchronizer implements Lock, java.io.Serializable {
|
||||
|
||||
@Override
|
||||
public boolean isHeldExclusively() {
|
||||
return getState() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAcquire(int acquires) {
|
||||
return compareAndSetState(0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryRelease(int releases) {
|
||||
setState(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition newCondition() {
|
||||
return new ConditionObject();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
|
||||
s.defaultReadObject();
|
||||
setState(0); // reset to unlocked state
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
acquire(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
return tryAcquire(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockInterruptibly() throws InterruptedException {
|
||||
acquireInterruptibly(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return tryAcquireNanos(1, unit.toNanos(timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
release(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
160
test/micro/org/openjdk/bench/java/util/concurrent/Maps.java
Normal file
160
test/micro/org/openjdk/bench/java/util/concurrent/Maps.java
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Threads;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public class Maps {
|
||||
private SimpleRandom rng;
|
||||
private Map<Integer, Integer> map;
|
||||
private Map<Integer, Integer> staticMap;
|
||||
private Integer[] key;
|
||||
|
||||
private int removesPerMaxRandom;
|
||||
private int insertsPerMaxRandom;
|
||||
private int total;
|
||||
private int position;
|
||||
|
||||
@Param("10000")
|
||||
private int nkeys;
|
||||
|
||||
@Setup
|
||||
public void initTest() {
|
||||
int pRemove = 10;
|
||||
int pInsert = 90;
|
||||
removesPerMaxRandom = (int) ((pRemove / 100.0 * 0x7FFFFFFFL));
|
||||
insertsPerMaxRandom = (int) ((pInsert / 100.0 * 0x7FFFFFFFL));
|
||||
|
||||
rng = new SimpleRandom();
|
||||
map = new ConcurrentHashMap<>();
|
||||
staticMap = new ConcurrentHashMap<>();
|
||||
total = 0;
|
||||
key = new Integer[nkeys];
|
||||
for (int i = 0; i < key.length; ++i) {
|
||||
key[i] = rng.next();
|
||||
staticMap.put(rng.next(), rng.next());
|
||||
}
|
||||
position = key.length / 2;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@Threads(4)
|
||||
public void testConcurrentHashMap() {
|
||||
int pos = position;
|
||||
// random-walk around key positions, bunching accesses
|
||||
int r = rng.next();
|
||||
pos += (r & 7) - 3;
|
||||
while (pos >= key.length) {
|
||||
pos -= key.length;
|
||||
}
|
||||
while (pos < 0) {
|
||||
pos += key.length;
|
||||
}
|
||||
Integer k = key[pos];
|
||||
Integer x = map.get(k);
|
||||
if (x != null) {
|
||||
if (x.intValue() != k.intValue()) {
|
||||
throw new Error("bad mapping: " + x + " to " + k);
|
||||
}
|
||||
|
||||
if (r < removesPerMaxRandom) {
|
||||
if (map.remove(k) != null) {
|
||||
pos = total % key.length; // move from position
|
||||
}
|
||||
}
|
||||
} else if (r < insertsPerMaxRandom) {
|
||||
++pos;
|
||||
map.put(k, k);
|
||||
}
|
||||
total += r;
|
||||
position = pos;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public ConcurrentHashMap<Integer, Integer> testConcurrentHashMapCopyConstructor() {
|
||||
return new ConcurrentHashMap<>(staticMap);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public ConcurrentHashMap<Integer, Integer> testConcurrentHashMapPutAll() {
|
||||
ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>(nkeys);
|
||||
for (int i = 0; i < nkeys; ++i) {
|
||||
map.put(rng.next(), rng.next());
|
||||
}
|
||||
map.putAll(staticMap);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testConcurrentHashMapIterators() {
|
||||
ConcurrentHashMap<Integer, Integer> map = (ConcurrentHashMap<Integer, Integer>) staticMap;
|
||||
int sum = 0;
|
||||
Enumeration it = map.elements();
|
||||
while (it.hasMoreElements()) {
|
||||
sum += (int) it.nextElement();
|
||||
}
|
||||
it = map.keys();
|
||||
while (it.hasMoreElements()) {
|
||||
sum += (int) it.nextElement();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static class SimpleRandom {
|
||||
private final static long multiplier = 0x5DEECE66DL;
|
||||
private final static long addend = 0xBL;
|
||||
private final static long mask = (1L << 48) - 1;
|
||||
private final static AtomicLong seq = new AtomicLong(1);
|
||||
private long seed = System.nanoTime() + seq.getAndIncrement();
|
||||
|
||||
public int next() {
|
||||
long nextSeed = (seed * multiplier + addend) & mask;
|
||||
seed = nextSeed;
|
||||
return ((int) (nextSeed >>> 17)) & 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
|
||||
/**
|
||||
* Generic problem for concurrency tests.
|
||||
*
|
||||
* @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
|
||||
*/
|
||||
public class Problem {
|
||||
|
||||
/*
|
||||
* Implementation notes:
|
||||
*
|
||||
* This problem makes its bidding to confuse loop unrolling and CSE, and as such break loop optimizations.
|
||||
* Should loop optimizations be allowed, the performance with different (l, r) could change non-linearly.
|
||||
*/
|
||||
|
||||
private final int[] data;
|
||||
private final int size;
|
||||
|
||||
public Problem(int size) {
|
||||
this.size = size;
|
||||
data = new int[size];
|
||||
}
|
||||
|
||||
public long solve() {
|
||||
return solve(0, size);
|
||||
}
|
||||
|
||||
public long solve(int l, int r) {
|
||||
long sum = 0;
|
||||
for (int c = l; c < r; c++) {
|
||||
int v = hash(data[c]);
|
||||
if (filter(v)) {
|
||||
sum += v;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public static int hash(int x) {
|
||||
x ^= (x << 21);
|
||||
x ^= (x >>> 31);
|
||||
x ^= (x << 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
public static boolean filter(int i) {
|
||||
return ((i & 0b101) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
import org.openjdk.jmh.infra.ThreadParams;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Tests the different blocking queues in the java.util.concurrent package.
|
||||
* The tests are done with a single producer and a variable number of consumers.
|
||||
* The tests are created from Doug Lea's concurrent test suite.
|
||||
*/
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public class ProducerConsumer {
|
||||
|
||||
@Param("100") // Will be expanded to at least the number of threads used
|
||||
private int capacity;
|
||||
|
||||
@Param
|
||||
private QueueType type;
|
||||
|
||||
private BlockingQueue<Integer> q;
|
||||
private Producer prod;
|
||||
|
||||
@Setup
|
||||
public void prepare(ThreadParams params) {
|
||||
capacity = Math.max(params.getThreadCount(), capacity);
|
||||
|
||||
switch (type) {
|
||||
case ABQ_F:
|
||||
q = new ArrayBlockingQueue<>(capacity, true);
|
||||
break;
|
||||
case ABQ_NF:
|
||||
q = new ArrayBlockingQueue<>(capacity, false);
|
||||
break;
|
||||
case LBQ:
|
||||
q = new LinkedBlockingQueue<>(capacity);
|
||||
break;
|
||||
case PBQ:
|
||||
q = new PriorityBlockingQueue<>(capacity);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
prod = new Producer(q);
|
||||
prod.start();
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void teardown() {
|
||||
prod.halt();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void test() {
|
||||
try {
|
||||
int last = -1;
|
||||
int v = q.take();
|
||||
if (v < last) {
|
||||
throw new Error("Out-of-Order transfer");
|
||||
}
|
||||
Blackhole.consumeCPU(10);
|
||||
} catch (Exception ie) {
|
||||
}
|
||||
}
|
||||
|
||||
public enum QueueType {
|
||||
LBQ,
|
||||
ABQ_NF,
|
||||
ABQ_F,
|
||||
PBQ,
|
||||
}
|
||||
|
||||
private class Producer extends Thread {
|
||||
private final BlockingQueue<Integer> queue;
|
||||
private int i = 0;
|
||||
private volatile boolean running;
|
||||
|
||||
public Producer(BlockingQueue<Integer> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
running = true;
|
||||
try {
|
||||
while (running) {
|
||||
queue.put(i++);
|
||||
}
|
||||
} catch (Exception ie) {
|
||||
}
|
||||
}
|
||||
|
||||
public void halt() {
|
||||
running = false;
|
||||
this.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
108
test/micro/org/openjdk/bench/java/util/concurrent/Queues.java
Normal file
108
test/micro/org/openjdk/bench/java/util/concurrent/Queues.java
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
import org.openjdk.jmh.infra.ThreadParams;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public class Queues {
|
||||
|
||||
@Param("100") // Will be expanded to at least the number of threads used
|
||||
private int capacity;
|
||||
|
||||
@Param
|
||||
private QueueType type;
|
||||
|
||||
public enum QueueType {
|
||||
LBQ,
|
||||
ABQ_NF,
|
||||
ABQ_F,
|
||||
PBQ,
|
||||
}
|
||||
|
||||
private BlockingQueue<Integer> q;
|
||||
|
||||
@Setup
|
||||
public void setup(ThreadParams params) {
|
||||
capacity = Math.max(params.getThreadCount(), capacity);
|
||||
|
||||
switch (type) {
|
||||
case ABQ_F:
|
||||
q = new ArrayBlockingQueue<>(capacity, true);
|
||||
break;
|
||||
case ABQ_NF:
|
||||
q = new ArrayBlockingQueue<>(capacity, false);
|
||||
break;
|
||||
case LBQ:
|
||||
q = new LinkedBlockingQueue<>(capacity);
|
||||
break;
|
||||
case PBQ:
|
||||
q = new PriorityBlockingQueue<>(capacity);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void test() {
|
||||
try {
|
||||
int l = (int) System.nanoTime();
|
||||
Integer item = q.poll();
|
||||
if (item != null) {
|
||||
Blackhole.consumeCPU(5);
|
||||
} else {
|
||||
Blackhole.consumeCPU(10);
|
||||
while (!q.offer(l)) {
|
||||
Blackhole.consumeCPU(5);
|
||||
}
|
||||
}
|
||||
} catch (Exception ie) {
|
||||
throw new Error("iteration failed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Warmup(iterations = 5, time = 1)
|
||||
@Measurement(iterations = 5, time = 1)
|
||||
@Fork(3)
|
||||
public class ThreadLocalRandomNextInt {
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class Global {
|
||||
public ThreadLocal<Random> tlr;
|
||||
private List<ThreadLocal<Integer>> contaminators; // reachable, non-garbage-collectable
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() {
|
||||
tlr = new ThreadLocal<Random>() {
|
||||
@Override
|
||||
protected Random initialValue() {
|
||||
return java.util.concurrent.ThreadLocalRandom.current();
|
||||
}
|
||||
};
|
||||
|
||||
// contaminate ThreadLocals
|
||||
int contaminatorCount = Integer.getInteger("contaminators", 0);
|
||||
contaminators = new ArrayList<>(contaminatorCount);
|
||||
for (int i = 0; i < contaminatorCount; i++) {
|
||||
final int finalI = i;
|
||||
ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
|
||||
@Override
|
||||
protected Integer initialValue() {
|
||||
return finalI;
|
||||
}
|
||||
};
|
||||
contaminators.add(tl);
|
||||
tl.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@State(Scope.Thread)
|
||||
public static class Local {
|
||||
public java.util.concurrent.ThreadLocalRandom tlr;
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() {
|
||||
tlr = java.util.concurrent.ThreadLocalRandom.current();
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int baseline(Local l) {
|
||||
return l.tlr.nextInt();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testJUC() {
|
||||
return java.util.concurrent.ThreadLocalRandom.current().nextInt();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int testLang(Global g) {
|
||||
return g.tlr.get().nextInt();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Threads;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Fork(1)
|
||||
@Threads(1)
|
||||
@Warmup(iterations = 5, time = 5)
|
||||
@Measurement(iterations = 5, time = 5)
|
||||
public class UnparkBenchSleepersAfter {
|
||||
|
||||
/*
|
||||
This micro creates thousands of sleeper threads after the threads doing the barrier await
|
||||
to see if that has any effect on the barrier performance.
|
||||
*/
|
||||
|
||||
@Param({"4000"})
|
||||
int idles;
|
||||
|
||||
@Param({"2"})
|
||||
int workers;
|
||||
|
||||
CyclicBarrier barrier;
|
||||
|
||||
@Benchmark
|
||||
public void barrier() throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(workers);
|
||||
for (int i = 0; i < workers; i++) {
|
||||
exec.submit(() ->
|
||||
{
|
||||
try {
|
||||
barrier.await();
|
||||
} catch (InterruptedException | BrokenBarrierException e) {
|
||||
barrier.reset();
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
latch.await();
|
||||
}
|
||||
|
||||
IdleRunnable[] idleRunnables;
|
||||
|
||||
ExecutorService exec;
|
||||
|
||||
@Setup
|
||||
public void setup() throws InterruptedException {
|
||||
barrier = new CyclicBarrier(workers);
|
||||
exec = Executors.newFixedThreadPool(workers);
|
||||
CountDownLatch latch = new CountDownLatch(workers);
|
||||
for (int i = 0; i < workers; i++) { // warmup exec
|
||||
exec.submit(() -> {
|
||||
try {
|
||||
Thread.sleep(0);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
latch.await();
|
||||
idleRunnables = new IdleRunnable[idles];
|
||||
for(int i = 0; i < idles; i++) {
|
||||
IdleRunnable r = new IdleRunnable();
|
||||
idleRunnables[i] = r;
|
||||
new Thread(r).start();
|
||||
}
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void tearDown() {
|
||||
for (IdleRunnable r : idleRunnables) {
|
||||
r.stop();
|
||||
}
|
||||
exec.shutdown();
|
||||
}
|
||||
|
||||
public static class IdleRunnable implements Runnable {
|
||||
volatile boolean done;
|
||||
Thread myThread;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
myThread = Thread.currentThread();
|
||||
while (!done) {
|
||||
LockSupport.park();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
done = true;
|
||||
LockSupport.unpark(myThread);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.util.concurrent;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Threads;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.infra.BenchmarkParams;
|
||||
import org.openjdk.jmh.infra.Control;
|
||||
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@Fork(1)
|
||||
@Threads(1)
|
||||
@Warmup(iterations = 5, time = 5)
|
||||
@Measurement(iterations = 5, time = 5)
|
||||
public class UnparkBenchSleepersBefore {
|
||||
|
||||
/*
|
||||
This micro creates thousands of sleeper threads before the threads doing the barrier await
|
||||
to see if that has any effect on the barrier performance, as seen with JDK-8305670.
|
||||
*/
|
||||
|
||||
@Param({"4000"})
|
||||
int idles;
|
||||
|
||||
@Param({"2"})
|
||||
int workers;
|
||||
|
||||
CyclicBarrier barrier;
|
||||
|
||||
@Benchmark
|
||||
public void barrier() throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(workers);
|
||||
for (int i = 0; i < workers; i++) {
|
||||
exec.submit(() -> {
|
||||
try {
|
||||
barrier.await();
|
||||
} catch (InterruptedException | BrokenBarrierException e) {
|
||||
barrier.reset();
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
latch.await();
|
||||
}
|
||||
|
||||
IdleRunnable[] idleRunnables;
|
||||
|
||||
ExecutorService exec;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
idleRunnables = new IdleRunnable[idles];
|
||||
for(int i = 0; i < idleRunnables.length; i++) {
|
||||
idleRunnables[i] = new IdleRunnable();
|
||||
new Thread(idleRunnables[i]).start();
|
||||
}
|
||||
barrier = new CyclicBarrier(workers);
|
||||
exec = Executors.newFixedThreadPool(workers); // order is important, create this executor only after idle threads
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void tearDown() {
|
||||
for (IdleRunnable r : idleRunnables) {
|
||||
r.stop();
|
||||
}
|
||||
exec.shutdown();
|
||||
}
|
||||
|
||||
public static class IdleRunnable implements Runnable {
|
||||
volatile boolean done;
|
||||
Thread myThread;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
myThread = Thread.currentThread();
|
||||
while (!done) {
|
||||
LockSupport.park();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
done = true;
|
||||
LockSupport.unpark(myThread);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue