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 }