8186670: Implement _onSpinWait() intrinsic for AArch64
Reviewed-by: phh, aph
This commit is contained in:
parent
3445e50bd5
commit
6954b98f8f
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
48
src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp
Normal file
48
src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
52
test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java
Normal file
52
test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java
Normal 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() {
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user