Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java
66646 views
1
/*
2
* Copyright (c) 2021, Amazon.com Inc. or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
package org.openjdk.bench.java.lang;
24
25
import org.openjdk.jmh.annotations.Benchmark;
26
import org.openjdk.jmh.annotations.BenchmarkMode;
27
import org.openjdk.jmh.annotations.Level;
28
import org.openjdk.jmh.annotations.Mode;
29
import org.openjdk.jmh.annotations.OutputTimeUnit;
30
import org.openjdk.jmh.annotations.Param;
31
import org.openjdk.jmh.annotations.Scope;
32
import org.openjdk.jmh.annotations.Setup;
33
import org.openjdk.jmh.annotations.State;
34
import org.openjdk.jmh.annotations.Threads;
35
36
import org.openjdk.jmh.infra.Blackhole;
37
38
import java.math.BigInteger;
39
import java.util.Random;
40
import java.util.concurrent.TimeUnit;
41
import java.util.function.BooleanSupplier;
42
43
/**
44
* This microbenchmark models producer-consumer.
45
*
46
* The microbenchmark uses two thread: 1 for a producer, 1 for a consumer.
47
* The microbenchmark uses BigInteger to have latencies of producing/consuming
48
* data comparable with synchronization operations.
49
*
50
* Thread.onSpinWait is used in a spin loop which is used to avoid heavy locks.
51
* In the spin loop volatile fields are checked. To reduce overhead accessing them
52
* they are only checked after a number of iterations.
53
*/
54
@BenchmarkMode(Mode.AverageTime)
55
@OutputTimeUnit(TimeUnit.MICROSECONDS)
56
@State(Scope.Benchmark)
57
@Threads(1)
58
public class ThreadOnSpinWaitProducerConsumer {
59
@Param({"100"})
60
public int maxNum;
61
62
@Param({"125"})
63
public int spinNum;
64
65
@Param({"10"})
66
public int checkSpinCondAfterIters;
67
68
@Param({"256"})
69
public int dataBitLength;
70
71
private Thread threadProducer;
72
private Thread threadConsumer;
73
private Object monitor;
74
75
private BigInteger a;
76
private BigInteger b;
77
private Blackhole bh;
78
79
private volatile int dataId;
80
private volatile int seenDataId;
81
82
private int producedDataCount;
83
private int consumedDataCount;
84
85
private void produceData() {
86
if (!isDataSeen()) {
87
return;
88
}
89
90
b = a.not();
91
++dataId;
92
++producedDataCount;
93
}
94
95
private void consumeData() {
96
if (isDataSeen()) {
97
return;
98
}
99
bh.consume(a.equals(b.not()));
100
seenDataId = dataId;
101
++consumedDataCount;
102
}
103
104
private boolean isDataSeen() {
105
return seenDataId == dataId;
106
}
107
108
private boolean isNewData() {
109
return seenDataId != dataId;
110
}
111
112
private boolean spinWaitForCondition(int spinNum, BooleanSupplier cond) {
113
for (int i = 0; i < spinNum; ++i) {
114
if ((i % checkSpinCondAfterIters) == 0 && cond.getAsBoolean()) {
115
return true;
116
}
117
Thread.onSpinWait();
118
}
119
return cond.getAsBoolean();
120
}
121
122
void produce() {
123
try {
124
while (dataId < maxNum) {
125
if (spinWaitForCondition(this.spinNum, this::isDataSeen)) {
126
synchronized (monitor) {
127
produceData();
128
monitor.notify();
129
}
130
} else {
131
synchronized (monitor) {
132
while (!isDataSeen()) {
133
monitor.wait();
134
}
135
136
produceData();
137
monitor.notify();
138
}
139
}
140
}
141
} catch (InterruptedException e) {}
142
}
143
144
void consume() {
145
try {
146
for (;;) {
147
if (spinWaitForCondition(this.spinNum, this::isNewData)) {
148
synchronized (monitor) {
149
consumeData();
150
monitor.notify();
151
}
152
} else {
153
synchronized (monitor) {
154
while (isDataSeen()) {
155
monitor.wait();
156
}
157
158
consumeData();
159
monitor.notify();
160
}
161
}
162
}
163
} catch (InterruptedException e) {}
164
}
165
166
@Setup(Level.Trial)
167
public void setup01() {
168
Random rnd = new Random(111);
169
a = BigInteger.probablePrime(dataBitLength, rnd);
170
monitor = new Object();
171
}
172
173
@Setup(Level.Invocation)
174
public void setup02() {
175
threadProducer = new Thread(this::produce);
176
threadConsumer = new Thread(this::consume);
177
}
178
179
@Benchmark
180
public void trial(Blackhole bh) throws Exception {
181
this.bh = bh;
182
producedDataCount = 0;
183
consumedDataCount = 0;
184
dataId = 0;
185
seenDataId = 0;
186
threadProducer.start();
187
threadConsumer.start();
188
threadProducer.join();
189
190
synchronized (monitor) {
191
while (!isDataSeen()) {
192
monitor.wait();
193
}
194
}
195
threadConsumer.interrupt();
196
197
if (producedDataCount != maxNum) {
198
throw new RuntimeException("Produced: " + producedDataCount + ". Expected: " + maxNum);
199
}
200
if (producedDataCount != consumedDataCount) {
201
throw new RuntimeException("produced != consumed: " + producedDataCount + " != " + consumedDataCount);
202
}
203
}
204
}
205
206