1 /*
2 * Copyright (c) 2023, Oracle and/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
24 package org.openjdk.bench.loom;
25
26 import org.openjdk.jmh.annotations.Benchmark;
27 import org.openjdk.jmh.annotations.BenchmarkMode;
28 import org.openjdk.jmh.annotations.Fork;
29 import org.openjdk.jmh.annotations.Level;
30 import org.openjdk.jmh.annotations.Measurement;
31 import org.openjdk.jmh.annotations.Mode;
32 import org.openjdk.jmh.annotations.OutputTimeUnit;
33 import org.openjdk.jmh.annotations.Param;
34 import org.openjdk.jmh.annotations.Scope;
35 import org.openjdk.jmh.annotations.Setup;
36 import org.openjdk.jmh.annotations.State;
37 import org.openjdk.jmh.annotations.Warmup;
38
39 import jdk.internal.vm.Continuation;
40 import jdk.internal.vm.ContinuationScope;
41
42 import java.util.concurrent.BlockingQueue;
43 import java.util.concurrent.SynchronousQueue;
44 import java.util.concurrent.locks.ReentrantLock;
45 import java.util.concurrent.TimeUnit;
46
47 //@Fork(2)
48 @Fork(1)
49 @Warmup(iterations = 3, time = 5)
50 @Measurement(iterations = 3, time = 5)
51 @BenchmarkMode(Mode.AverageTime)
52 @OutputTimeUnit(TimeUnit.NANOSECONDS)
53 @State(Scope.Thread)
54 @SuppressWarnings("preview")
55 public class Monitors {
56 static final ContinuationScope SCOPE = new ContinuationScope() { };
57
58 static class RunSynchronized implements Runnable {
59 private final int stackDepth;
60 private final Object[] syncLockArray;
61
62 private RunSynchronized(int stackDepth, ReentrantLock[] lockArray) {
63 this.stackDepth = stackDepth;
64 this.syncLockArray = lockArray;
65 }
66
67 public void recursive(int depth) {
68 if (syncLockArray[depth] != null) {
69 synchronized (syncLockArray[depth]) {
70 if (depth > 0) {
71 recursive(depth - 1);
72 } else {
73 Continuation.yield(SCOPE);
74 }
75 }
76 } else {
77 if (depth > 0) {
78 recursive(depth - 1);
79 } else {
80 Continuation.yield(SCOPE);
81 }
82 }
83 }
84
85 @Override
86 public void run() {
87 while (true) {
88 recursive(stackDepth);
89 }
90 }
91 }
92
93 static class RunReentrantLock implements Runnable {
94 private final int stackDepth;
95 private final ReentrantLock[] lockArray;
96
97 private RunReentrantLock(int stackDepth, ReentrantLock[] lockArray) {
98 this.stackDepth = stackDepth;
99 this.lockArray = lockArray;
100 }
101
102 public void recursive(int depth) {
103 if (lockArray[depth] != null) {
104 lockArray[depth].lock();
105 if (depth > 0) {
106 recursive(depth - 1);
107 } else {
108 Continuation.yield(SCOPE);
109 }
110 lockArray[depth].unlock();
111 } else {
112 if (depth > 0) {
113 recursive(depth - 1);
114 } else {
115 Continuation.yield(SCOPE);
116 }
117 }
118 }
119
120 @Override
121 public void run() {
122 while (true) {
123 recursive(stackDepth);
124 }
125 }
126 }
127
128 static void inflateAllLocks(ReentrantLock[] lockArray) {
129 for (ReentrantLock lock : lockArray) {
130 if (lock != null) {
131 synchronized (lock) {
132 try {
133 lock.wait(1);
134 } catch (Exception e) {}
135 }
136 }
137 }
138 }
139
140 static ReentrantLock[] createLockArray(int stackDepth, int monitorCount) {
141 ReentrantLock[] lockArray = new ReentrantLock[stackDepth + 1];
142
143 // Always a lock on the bottom most frame
144 lockArray[stackDepth] = new ReentrantLock();
145 // Limit extra monitors to no more than one per recursive frame.
146 int remainingMonitors = Math.min(Math.max(0, monitorCount - 1), stackDepth);
147
148 if (remainingMonitors > 0) {
149 // Calculate which other frames will use a lock.
150 int stride = Math.max(2, (stackDepth / monitorCount));
151 int frameIndex = (stackDepth - stride) % (stackDepth + 1);
152 while (remainingMonitors > 0) {
153 if (lockArray[frameIndex] == null) {
154 lockArray[frameIndex] = new ReentrantLock();
155 frameIndex = (frameIndex - stride) % (stackDepth + 1);
156 remainingMonitors--;
157 } else {
158 frameIndex = (frameIndex - 1) % (stackDepth + 1);
159 }
160 }
161 }
162 System.out.println("Created lock array. synchronized/ReentrantLock will be used at following indexes:");
163 for (int i = stackDepth; i >= 0; i--) {
164 System.out.println("[" + i + "] : " + (lockArray[i] != null ? "synchronized/ReentrantLock" : "-"));
165 }
166 return lockArray;
167 }
168
169 static Continuation createContinuation(int stackDepth, ReentrantLock[] lockArray, boolean useSyncronized) {
170 Runnable task = useSyncronized ? new RunSynchronized(stackDepth, lockArray)
171 : new RunReentrantLock(stackDepth, lockArray);
172 //inflateAllLocks(lockArray);
173 return new Continuation(SCOPE, task);
174 }
175
176 @State(Scope.Thread)
177 public static class MyState {
178 @Param({"0", "5", "10", "20"})
179 public int stackDepth;
180
181 @Param({"1", "2", "3"})
182 public int monitorCount;
183
184 public Continuation syncCont;
185 public Continuation reentCont;
186
187 @Setup(Level.Trial)
188 public void doSetup() {
189 ReentrantLock[] lockArray = createLockArray(stackDepth, monitorCount);
190 // We don't care which object we synchronize on so just pass
191 // the same ReentrantLock array to the synchronized task.
192 syncCont = createContinuation(stackDepth, lockArray, true /* useSyncronized */);
193 reentCont = createContinuation(stackDepth, lockArray, false /* useSyncronized */);
194 System.out.println("Created continuation used for testing with stackDepth: " + stackDepth + " and monitorCount: " + monitorCount);
195 }
196 }
197
198 @Benchmark
199 public void syncLock(MyState state) {
200 state.syncCont.run();
201 }
202
203 @Benchmark
204 public void reentrantLock(MyState state) {
205 state.reentCont.run();
206 }
207 }