1 /* 2 * Copyright (c) 2021, 2022, 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.java.lang.foreign; 25 26 import java.lang.foreign.*; 27 28 import org.openjdk.jmh.annotations.Benchmark; 29 import org.openjdk.jmh.annotations.BenchmarkMode; 30 import org.openjdk.jmh.annotations.Fork; 31 import org.openjdk.jmh.annotations.Setup; 32 import org.openjdk.jmh.annotations.Param; 33 import org.openjdk.jmh.annotations.TearDown; 34 import org.openjdk.jmh.annotations.Measurement; 35 import org.openjdk.jmh.annotations.Mode; 36 import org.openjdk.jmh.annotations.OutputTimeUnit; 37 import org.openjdk.jmh.annotations.State; 38 import org.openjdk.jmh.annotations.Warmup; 39 40 import java.lang.foreign.MemorySegment.Scope; 41 import java.lang.invoke.MethodHandle; 42 import java.util.concurrent.TimeUnit; 43 44 import static java.lang.foreign.ValueLayout.JAVA_BYTE; 45 46 @BenchmarkMode(Mode.AverageTime) 47 @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) 48 @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) 49 @State(org.openjdk.jmh.annotations.Scope.Thread) 50 @OutputTimeUnit(TimeUnit.NANOSECONDS) 51 @Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" }) 52 public class StrLenTest extends CLayouts { 53 54 Arena arena = Arena.ofConfined(); 55 56 SegmentAllocator segmentAllocator; 57 SegmentAllocator arenaAllocator = new RingAllocator(arena); 58 SlicingPool pool = new SlicingPool(); 59 60 @Param({"5", "20", "100"}) 61 public int size; 62 public String str; 63 64 static { 65 System.loadLibrary("StrLen"); 66 } 67 68 static final MethodHandle STRLEN; 69 70 static { 71 Linker abi = Linker.nativeLinker(); 72 STRLEN = abi.downcallHandle(abi.defaultLookup().find("strlen").get(), 73 FunctionDescriptor.of(C_INT, C_POINTER)); 74 } 75 76 @Setup 77 public void setup() { 78 str = makeString(size); 79 segmentAllocator = SegmentAllocator.prefixAllocator(arena.allocate(size + 1, 1)); 80 } 81 82 @TearDown 83 public void tearDown() { 84 arena.close(); 85 } 86 87 @Benchmark 88 public int jni_strlen() throws Throwable { 89 return strlen(str); 90 } 91 92 @Benchmark 93 public int panama_strlen() throws Throwable { 94 try (Arena arena = Arena.ofConfined()) { 95 MemorySegment segment = arena.allocateFrom(str); 96 return (int)STRLEN.invokeExact(segment); 97 } 98 } 99 100 @Benchmark 101 public int panama_strlen_ring() throws Throwable { 102 return (int)STRLEN.invokeExact(arenaAllocator.allocateFrom(str)); 103 } 104 105 @Benchmark 106 public int panama_strlen_pool() throws Throwable { 107 Arena arena = pool.acquire(); 108 int l = (int) STRLEN.invokeExact(arena.allocateFrom(str)); 109 arena.close(); 110 return l; 111 } 112 113 @Benchmark 114 public int panama_strlen_prefix() throws Throwable { 115 return (int)STRLEN.invokeExact(segmentAllocator.allocateFrom(str)); 116 } 117 118 @Benchmark 119 public int panama_strlen_unsafe() throws Throwable { 120 MemorySegment address = makeStringUnsafe(str); 121 int res = (int) STRLEN.invokeExact(address); 122 freeMemory(address); 123 return res; 124 } 125 126 static MemorySegment makeStringUnsafe(String s) { 127 byte[] bytes = s.getBytes(); 128 int len = bytes.length; 129 MemorySegment address = allocateMemory(len + 1); 130 MemorySegment str = address.asSlice(0, len + 1); 131 str.copyFrom(MemorySegment.ofArray(bytes)); 132 str.set(JAVA_BYTE, len, (byte)0); 133 return address; 134 } 135 136 static native int strlen(String str); 137 138 static String makeString(int size) { 139 String lorem = """ 140 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et 141 dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip 142 ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 143 fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt 144 mollit anim id est laborum. 145 """; 146 return lorem.substring(0, size); 147 } 148 149 static class RingAllocator implements SegmentAllocator { 150 final MemorySegment segment; 151 SegmentAllocator current; 152 long rem; 153 154 public RingAllocator(Arena session) { 155 this.segment = session.allocate(1024, 1); 156 reset(); 157 } 158 159 @Override 160 public MemorySegment allocate(long byteSize, long byteAlignment) { 161 if (rem < byteSize) { 162 reset(); 163 } 164 MemorySegment res = current.allocate(byteSize, byteAlignment); 165 long lastOffset = res.address() - segment.address() + res.byteSize(); 166 rem = segment.byteSize() - lastOffset; 167 return res; 168 } 169 170 void reset() { 171 current = SegmentAllocator.slicingAllocator(segment); 172 rem = segment.byteSize(); 173 } 174 } 175 176 static class SlicingPool { 177 final MemorySegment pool = Arena.ofAuto().allocate(1024); 178 boolean isAcquired = false; 179 180 public Arena acquire() { 181 if (isAcquired) { 182 throw new IllegalStateException("An allocator is already in use"); 183 } 184 isAcquired = true; 185 return new SlicingPoolAllocator(); 186 } 187 188 class SlicingPoolAllocator implements Arena { 189 190 final Arena arena = Arena.ofConfined(); 191 final SegmentAllocator slicing = SegmentAllocator.slicingAllocator(pool); 192 193 public MemorySegment allocate(long byteSize, long byteAlignment) { 194 return slicing.allocate(byteSize, byteAlignment) 195 .reinterpret(arena, null); 196 } 197 198 @Override 199 public Scope scope() { 200 return arena.scope(); 201 } 202 203 public void close() { 204 isAcquired = false; 205 arena.close(); 206 } 207 } 208 } 209 }