8186670: Implement _onSpinWait() intrinsic for AArch64

Reviewed-by: phh, aph
This commit is contained in:
Evgeny Astigeevich 2021-11-11 22:23:35 +00:00 committed by Paul Hohensee
parent 3445e50bd5
commit 6954b98f8f
13 changed files with 766 additions and 2 deletions

View File

@ -2373,6 +2373,8 @@ const bool Matcher::match_rule_supported(int opcode) {
bool ret_value = true;
switch (opcode) {
case Op_OnSpinWait:
return VM_Version::supports_on_spin_wait();
case Op_CacheWB:
case Op_CacheWBPreSync:
case Op_CacheWBPostSync:
@ -14333,6 +14335,18 @@ instruct signumF_reg(vRegF dst, vRegF src, vRegF zero, vRegF one) %{
ins_pipe(fp_uop_d);
%}
instruct onspinwait() %{
match(OnSpinWait);
ins_cost(INSN_COST);
format %{ "onspinwait" %}
ins_encode %{
__ spin_wait();
%}
ins_pipe(pipe_class_empty);
%}
// ============================================================================
// Logical Instructions

View File

@ -2984,7 +2984,7 @@ void LIR_Assembler::membar_loadstore() { __ membar(MacroAssembler::LoadStore); }
void LIR_Assembler::membar_storeload() { __ membar(MacroAssembler::StoreLoad); }
void LIR_Assembler::on_spin_wait() {
Unimplemented();
__ spin_wait();
}
void LIR_Assembler::get_thread(LIR_Opr result_reg) {

View File

@ -110,7 +110,15 @@ define_pd_global(intx, InlineSmallCode, 1000);
product(int, SoftwarePrefetchHintDistance, -1, \
"Use prfm hint with specified distance in compiled code." \
"Value -1 means off.") \
range(-1, 4096)
range(-1, 4096) \
product(ccstr, OnSpinWaitInst, "none", DIAGNOSTIC, \
"The instruction to use to implement " \
"java.lang.Thread.onSpinWait()." \
"Options: none, nop, isb, yield.") \
product(uint, OnSpinWaitInstCount, 1, DIAGNOSTIC, \
"The number of OnSpinWaitInst instructions to generate." \
"It cannot be used with OnSpinWaitInst=none.") \
range(1, 99)
// end of ARCH_FLAGS

View File

@ -5154,3 +5154,21 @@ void MacroAssembler::verify_cross_modify_fence_not_required() {
}
}
#endif
void MacroAssembler::spin_wait() {
for (int i = 0; i < VM_Version::spin_wait_desc().inst_count(); ++i) {
switch (VM_Version::spin_wait_desc().inst()) {
case SpinWait::NOP:
nop();
break;
case SpinWait::ISB:
isb();
break;
case SpinWait::YIELD:
yield();
break;
default:
ShouldNotReachHere();
}
}
}

View File

@ -1402,6 +1402,9 @@ public:
void cache_wb(Address line);
void cache_wbsync(bool is_pre);
// Code for java.lang.Thread::onSpinWait() intrinsic.
void spin_wait();
private:
// Check the current thread doesn't need a cross modify fence.
void verify_cross_modify_fence_not_required() PRODUCT_RETURN;

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2021, 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.
*
*/
#ifndef CPU_AARCH64_SPIN_WAIT_AARCH64_HPP
#define CPU_AARCH64_SPIN_WAIT_AARCH64_HPP
class SpinWait {
public:
enum Inst {
NONE = -1,
NOP,
ISB,
YIELD
};
private:
Inst _inst;
int _count;
public:
SpinWait(Inst inst = NONE, int count = 0) : _inst(inst), _count(count) {}
Inst inst() const { return _inst; }
int inst_count() const { return _count; }
};
#endif // CPU_AARCH64_SPIN_WAIT_AARCH64_HPP

View File

@ -46,6 +46,26 @@ int VM_Version::_dcache_line_size;
int VM_Version::_icache_line_size;
int VM_Version::_initial_sve_vector_length;
SpinWait VM_Version::_spin_wait;
static SpinWait get_spin_wait_desc() {
if (strcmp(OnSpinWaitInst, "nop") == 0) {
return SpinWait(SpinWait::NOP, OnSpinWaitInstCount);
} else if (strcmp(OnSpinWaitInst, "isb") == 0) {
return SpinWait(SpinWait::ISB, OnSpinWaitInstCount);
} else if (strcmp(OnSpinWaitInst, "yield") == 0) {
return SpinWait(SpinWait::YIELD, OnSpinWaitInstCount);
} else if (strcmp(OnSpinWaitInst, "none") != 0) {
vm_exit_during_initialization("The options for OnSpinWaitInst are nop, isb, yield, and none", OnSpinWaitInst);
}
if (!FLAG_IS_DEFAULT(OnSpinWaitInstCount) && OnSpinWaitInstCount > 0) {
vm_exit_during_initialization("OnSpinWaitInstCount cannot be used for OnSpinWaitInst 'none'");
}
return SpinWait{};
}
void VM_Version::initialize() {
_supports_cx8 = true;
_supports_atomic_getset4 = true;
@ -451,5 +471,7 @@ void VM_Version::initialize() {
}
#endif
_spin_wait = get_spin_wait_desc();
UNSUPPORTED_OPTION(CriticalJNINatives);
}

View File

@ -26,6 +26,7 @@
#ifndef CPU_AARCH64_VM_VERSION_AARCH64_HPP
#define CPU_AARCH64_VM_VERSION_AARCH64_HPP
#include "spin_wait_aarch64.hpp"
#include "runtime/abstract_vm_version.hpp"
#include "utilities/sizes.hpp"
@ -45,6 +46,8 @@ protected:
static int _icache_line_size;
static int _initial_sve_vector_length;
static SpinWait _spin_wait;
// Read additional info using OS-specific interfaces
static void get_os_cpu_info();
@ -142,6 +145,10 @@ public:
static void get_compatible_board(char *buf, int buflen);
static const SpinWait& spin_wait_desc() { return _spin_wait; }
static bool supports_on_spin_wait() { return _spin_wait.inst() != SpinWait::NONE; }
#ifdef __APPLE__
// Is the CPU running emulated (for example macOS Rosetta running x86_64 code on M1 ARM (aarch64)
static bool is_cpu_emulated();

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2021, 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.
*/
/**
* @test TestOnSpinWaitAArch64
* @summary Checks that java.lang.Thread.onSpinWait is intrinsified with instructions specified with '-XX:OnSpinWaitInst' and '-XX:OnSpinWaitInstCount'
* @bug 8186670
* @library /test/lib
*
* @requires vm.flagless
* @requires os.arch=="aarch64"
*
* @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c2 nop 7
* @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c2 isb 3
* @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c2 yield 1
* @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c1 nop 7
* @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c1 isb 3
* @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c1 yield
*/
package compiler.onSpinWait;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
public class TestOnSpinWaitAArch64 {
public static void main(String[] args) throws Exception {
String compiler = args[0];
String spinWaitInst = args[1];
String spinWaitInstCount = (args.length == 3) ? args[2] : "1";
ArrayList<String> command = new ArrayList<String>();
command.add("-XX:+IgnoreUnrecognizedVMOptions");
command.add("-showversion");
command.add("-XX:-BackgroundCompilation");
command.add("-XX:+UnlockDiagnosticVMOptions");
command.add("-XX:+PrintAssembly");
if (compiler.equals("c2")) {
command.add("-XX:-TieredCompilation");
} else if (compiler.equals("c1")) {
command.add("-XX:+TieredCompilation");
command.add("-XX:TieredStopAtLevel=1");
} else {
throw new RuntimeException("Unknown compiler: " + compiler);
}
command.add("-Xbatch");
command.add("-XX:OnSpinWaitInst=" + spinWaitInst);
command.add("-XX:OnSpinWaitInstCount=" + spinWaitInstCount);
command.add("-XX:CompileCommand=compileonly," + Launcher.class.getName() + "::" + "test");
command.add(Launcher.class.getName());
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(command);
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
analyzer.shouldHaveExitValue(0);
System.out.println(analyzer.getOutput());
checkOutput(analyzer, spinWaitInst, Integer.parseInt(spinWaitInstCount));
}
private static String getSpinWaitInstHex(String spinWaitInst) {
if ("nop".equals(spinWaitInst)) {
return "1f20 03d5";
} else if ("isb".equals(spinWaitInst)) {
return "df3f 03d5";
} else if ("yield".equals(spinWaitInst)) {
return "3f20 03d5";
} else {
throw new RuntimeException("Unknown spin wait instruction: " + spinWaitInst);
}
}
private static void addInstrs(String line, ArrayList<String> instrs) {
for (String instr : line.split("\\|")) {
instrs.add(instr.trim());
}
}
// The expected output of PrintAssembly for example for a spin wait with three NOPs:
//
// # {method} {0x0000ffff6ac00370} 'test' '()V' in 'compiler/onSpinWait/TestOnSpinWaitAArch64$Launcher'
// # [sp+0x40] (sp of caller)
// 0x0000ffff9d557680: 1f20 03d5 | e953 40d1 | 3f01 00f9 | ff03 01d1 | fd7b 03a9 | 1f20 03d5 | 1f20 03d5
//
// 0x0000ffff9d5576ac: ;*invokestatic onSpinWait {reexecute=0 rethrow=0 return_oop=0}
// ; - compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@0 (line 161)
// 0x0000ffff9d5576ac: 1f20 03d5 | fd7b 43a9 | ff03 0191
//
// The checkOutput method adds hex instructions before 'invokestatic onSpinWait' and from the line after
// it to a list. The list is traversed from the end to count spin wait instructions.
//
// If JVM finds the hsdis library the output is like:
//
// # {method} {0x0000ffff63000370} 'test' '()V' in 'compiler/onSpinWait/TestOnSpinWaitAArch64$Launcher'
// # [sp+0x20] (sp of caller)
// 0x0000ffffa409da80: nop
// 0x0000ffffa409da84: sub sp, sp, #0x20
// 0x0000ffffa409da88: stp x29, x30, [sp, #16] ;*synchronization entry
// ; - compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@-1 (line 187)
// 0x0000ffffa409da8c: nop
// 0x0000ffffa409da90: nop
// 0x0000ffffa409da94: nop
// 0x0000ffffa409da98: nop
// 0x0000ffffa409da9c: nop
// 0x0000ffffa409daa0: nop
// 0x0000ffffa409daa4: nop ;*invokestatic onSpinWait {reexecute=0 rethrow=0 return_oop=0}
// ; - compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@0 (line 187)
private static void checkOutput(OutputAnalyzer output, String spinWaitInst, int spinWaitInstCount) {
Iterator<String> iter = output.asLines().listIterator();
String match = skipTo(iter, "'test' '()V' in 'compiler/onSpinWait/TestOnSpinWaitAArch64$Launcher'");
if (match == null) {
throw new RuntimeException("Missing compiler output for the method compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test");
}
ArrayList<String> instrs = new ArrayList<String>();
String line = null;
boolean hasHexInstInOutput = false;
while (iter.hasNext()) {
line = iter.next();
if (line.contains("*invokestatic onSpinWait")) {
break;
}
if (!hasHexInstInOutput) {
hasHexInstInOutput = line.contains("|");
}
if (line.contains("0x") && !line.contains(";")) {
addInstrs(line, instrs);
}
}
if (!iter.hasNext() || !iter.next().contains("- compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@0") || !iter.hasNext()) {
throw new RuntimeException("Missing compiler output for Thread.onSpinWait intrinsic");
}
String strToSearch = null;
if (!hasHexInstInOutput) {
instrs.add(line.split(";")[0].trim());
strToSearch = spinWaitInst;
} else {
line = iter.next();
if (!line.contains("0x") || line.contains(";")) {
throw new RuntimeException("Expected hex instructions");
}
addInstrs(line, instrs);
strToSearch = getSpinWaitInstHex(spinWaitInst);
}
int foundInstCount = 0;
ListIterator<String> instrReverseIter = instrs.listIterator(instrs.size());
while (instrReverseIter.hasPrevious()) {
if (instrReverseIter.previous().endsWith(strToSearch)) {
foundInstCount = 1;
break;
}
}
while (instrReverseIter.hasPrevious()) {
if (!instrReverseIter.previous().endsWith(strToSearch)) {
break;
}
++foundInstCount;
}
if (foundInstCount != spinWaitInstCount) {
throw new RuntimeException("Wrong instruction " + strToSearch + " count " + foundInstCount + "!\n -- expecting " + spinWaitInstCount);
}
}
private static String skipTo(Iterator<String> iter, String substring) {
while (iter.hasNext()) {
String nextLine = iter.next();
if (nextLine.contains(substring)) {
return nextLine;
}
}
return null;
}
static class Launcher {
public static void main(final String[] args) throws Exception {
int end = 20_000;
for (int i=0; i < end; i++) {
test();
}
}
static void test() {
java.lang.Thread.onSpinWait();
}
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2021, 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.
*/
/**
* @test TestOnSpinWaitNoneAArch64
* @summary Checks that java.lang.Thread.onSpinWait is not intrinsified when '-XX:OnSpinWaitInst=none' is used
* @bug 8186670
* @library /test/lib
*
* @requires vm.flagless
* @requires os.arch=="aarch64"
*
* @run driver compiler.onSpinWait.TestOnSpinWaitNoneAArch64
*/
package compiler.onSpinWait;
import java.util.ArrayList;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
public class TestOnSpinWaitNoneAArch64 {
public static void main(String[] args) throws Exception {
ArrayList<String> command = new ArrayList<String>();
command.add("-XX:+IgnoreUnrecognizedVMOptions");
command.add("-showversion");
command.add("-XX:-TieredCompilation");
command.add("-Xbatch");
command.add("-XX:+PrintCompilation");
command.add("-XX:+UnlockDiagnosticVMOptions");
command.add("-XX:+PrintInlining");
command.add("-XX:OnSpinWaitInst=none");
command.add(Launcher.class.getName());
// Test C2 compiler
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(command);
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
analyzer.shouldHaveExitValue(0);
// The test is applicable only to C2 (present in Server VM).
if (analyzer.getStderr().contains("Server VM")) {
analyzer.shouldNotContain("java.lang.Thread::onSpinWait (1 bytes) (intrinsic)");
}
}
static class Launcher {
public static void main(final String[] args) throws Exception {
int end = 20_000;
for (int i=0; i < end; i++) {
test();
}
}
static void test() {
java.lang.Thread.onSpinWait();
}
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2021, 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.lang;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Threads;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class ThreadOnSpinWait {
@Benchmark
@Threads(1)
public void testOnSpinWait() {
Thread.onSpinWait();
}
@Benchmark
@Threads(1)
public void testSleep0() throws InterruptedException {
Thread.sleep(0);
}
@Benchmark
@Threads(1)
public void testEmpty() {
}
}

View File

@ -0,0 +1,204 @@
/*
* Copyright (c) 2021, 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.lang;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
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.infra.Blackhole;
import java.math.BigInteger;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
/**
* This microbenchmark models producer-consumer.
*
* The microbenchmark uses two thread: 1 for a producer, 1 for a consumer.
* The microbenchmark uses BigInteger to have latencies of producing/consuming
* data comparable with synchronization operations.
*
* Thread.onSpinWait is used in a spin loop which is used to avoid heavy locks.
* In the spin loop volatile fields are checked. To reduce overhead accessing them
* they are only checked after a number of iterations.
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
@Threads(1)
public class ThreadOnSpinWaitProducerConsumer {
@Param({"100"})
public int maxNum;
@Param({"125"})
public int spinNum;
@Param({"10"})
public int checkSpinCondAfterIters;
@Param({"256"})
public int dataBitLength;
private Thread threadProducer;
private Thread threadConsumer;
private Object monitor;
private BigInteger a;
private BigInteger b;
private Blackhole bh;
private volatile int dataId;
private volatile int seenDataId;
private int producedDataCount;
private int consumedDataCount;
private void produceData() {
if (!isDataSeen()) {
return;
}
b = a.not();
++dataId;
++producedDataCount;
}
private void consumeData() {
if (isDataSeen()) {
return;
}
bh.consume(a.equals(b.not()));
seenDataId = dataId;
++consumedDataCount;
}
private boolean isDataSeen() {
return seenDataId == dataId;
}
private boolean isNewData() {
return seenDataId != dataId;
}
private boolean spinWaitForCondition(int spinNum, BooleanSupplier cond) {
for (int i = 0; i < spinNum; ++i) {
if ((i % checkSpinCondAfterIters) == 0 && cond.getAsBoolean()) {
return true;
}
Thread.onSpinWait();
}
return cond.getAsBoolean();
}
void produce() {
try {
while (dataId < maxNum) {
if (spinWaitForCondition(this.spinNum, this::isDataSeen)) {
synchronized (monitor) {
produceData();
monitor.notify();
}
} else {
synchronized (monitor) {
while (!isDataSeen()) {
monitor.wait();
}
produceData();
monitor.notify();
}
}
}
} catch (InterruptedException e) {}
}
void consume() {
try {
for (;;) {
if (spinWaitForCondition(this.spinNum, this::isNewData)) {
synchronized (monitor) {
consumeData();
monitor.notify();
}
} else {
synchronized (monitor) {
while (isDataSeen()) {
monitor.wait();
}
consumeData();
monitor.notify();
}
}
}
} catch (InterruptedException e) {}
}
@Setup(Level.Trial)
public void setup01() {
Random rnd = new Random(111);
a = BigInteger.probablePrime(dataBitLength, rnd);
monitor = new Object();
}
@Setup(Level.Invocation)
public void setup02() {
threadProducer = new Thread(this::produce);
threadConsumer = new Thread(this::consume);
}
@Benchmark
public void trial(Blackhole bh) throws Exception {
this.bh = bh;
producedDataCount = 0;
consumedDataCount = 0;
dataId = 0;
seenDataId = 0;
threadProducer.start();
threadConsumer.start();
threadProducer.join();
synchronized (monitor) {
while (!isDataSeen()) {
monitor.wait();
}
}
threadConsumer.interrupt();
if (producedDataCount != maxNum) {
throw new RuntimeException("Produced: " + producedDataCount + ". Expected: " + maxNum);
}
if (producedDataCount != consumedDataCount) {
throw new RuntimeException("produced != consumed: " + producedDataCount + " != " + consumedDataCount);
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2021, Red Hat Inc. 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.lang;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
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 java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class ThreadOnSpinWaitSharedCounter {
@Param({"1000000"})
public int maxNum;
@Param({"4"})
public int threadCount;
AtomicInteger theCounter;
Thread threads[];
void work() {
for (;;) {
int prev = theCounter.get();
if (prev >= maxNum) {
break;
}
if (theCounter.compareAndExchange(prev, prev + 1) != prev) {
Thread.onSpinWait();
}
}
}
@Setup(Level.Trial)
public void foo() {
theCounter = new AtomicInteger();
}
@Setup(Level.Invocation)
public void setup() {
theCounter.set(0);
threads = new Thread[threadCount];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(this::work);
}
}
@Benchmark
public void trial() throws Exception {
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
}
}