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 package org.openjdk.bench.java.lang.foreign; 24 25 import org.openjdk.jmh.annotations.Benchmark; 26 import org.openjdk.jmh.annotations.BenchmarkMode; 27 import org.openjdk.jmh.annotations.Fork; 28 import org.openjdk.jmh.annotations.Measurement; 29 import org.openjdk.jmh.annotations.Mode; 30 import org.openjdk.jmh.annotations.OutputTimeUnit; 31 import org.openjdk.jmh.annotations.Param; 32 import org.openjdk.jmh.annotations.Scope; 33 import org.openjdk.jmh.annotations.Setup; 34 import org.openjdk.jmh.annotations.State; 35 import org.openjdk.jmh.annotations.Warmup; 36 37 import java.lang.foreign.Arena; 38 import java.lang.foreign.MemorySegment; 39 import java.lang.foreign.ValueLayout; 40 import java.util.concurrent.ThreadLocalRandom; 41 import java.util.concurrent.TimeUnit; 42 import java.util.stream.IntStream; 43 import java.util.stream.Stream; 44 45 import static java.lang.foreign.ValueLayout.*; 46 import static jdk.internal.foreign.StringSupport.*; 47 48 @BenchmarkMode(Mode.AverageTime) 49 @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) 50 @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) 51 @State(Scope.Benchmark) 52 @OutputTimeUnit(TimeUnit.NANOSECONDS) 53 @Fork(value = 3, jvmArgsAppend = {"--add-exports=java.base/jdk.internal.foreign=ALL-UNNAMED", "--enable-native-access=ALL-UNNAMED", "--enable-preview"}) 54 public class InternalStrLen { 55 56 private MemorySegment singleByteSegment; 57 private MemorySegment singleByteSegmentMisaligned; 58 private MemorySegment doubleByteSegment; 59 private MemorySegment quadByteSegment; 60 61 @Param({"1", "4", "16", "251", "1024"}) 62 int size; 63 64 @Setup 65 public void setup() { 66 var arena = Arena.ofAuto(); 67 singleByteSegment = arena.allocate((size + 1L) * Byte.BYTES); 68 singleByteSegmentMisaligned = arena.allocate((size + 1L) * Byte.BYTES); 69 doubleByteSegment = arena.allocate((size + 1L) * Short.BYTES); 70 quadByteSegment = arena.allocate((size + 1L) * Integer.BYTES); 71 Stream.of(singleByteSegment, doubleByteSegment, quadByteSegment) 72 .forEach(s -> IntStream.range(0, (int) s.byteSize() - 1) 73 .forEach(i -> s.set( 74 ValueLayout.JAVA_BYTE, 75 i, 76 (byte) ThreadLocalRandom.current().nextInt(1, 254) 77 ))); 78 singleByteSegment.set(ValueLayout.JAVA_BYTE, singleByteSegment.byteSize() - Byte.BYTES, (byte) 0); 79 doubleByteSegment.set(ValueLayout.JAVA_SHORT, doubleByteSegment.byteSize() - Short.BYTES, (short) 0); 80 quadByteSegment.set(ValueLayout.JAVA_INT, quadByteSegment.byteSize() - Integer.BYTES, 0); 81 singleByteSegmentMisaligned = arena.allocate(singleByteSegment.byteSize() + 1). 82 asSlice(1); 83 MemorySegment.copy(singleByteSegment, 0, singleByteSegmentMisaligned, 0, singleByteSegment.byteSize()); 84 } 85 86 @Benchmark 87 public int elementSingle() { 88 return legacy_strlen_byte(singleByteSegment, 0); 89 } 90 91 @Benchmark 92 public int elementByteMisaligned() { 93 return legacy_strlen_byte(singleByteSegmentMisaligned, 0); 94 } 95 96 @Benchmark 97 public int elementDouble() { 98 return legacy_strlen_short(doubleByteSegment, 0); 99 } 100 101 @Benchmark 102 public int elementQuad() { 103 return legacy_strlen_int(quadByteSegment, 0); 104 } 105 106 @Benchmark 107 public int chunkedSingle() { 108 return chunkedStrlenByte(singleByteSegment, 0); 109 } 110 111 @Benchmark 112 public int chunkedSingleMisaligned() { 113 return chunkedStrlenByte(singleByteSegmentMisaligned, 0); 114 } 115 116 @Benchmark 117 public int chunkedDouble() { 118 return chunkedStrlenShort(doubleByteSegment, 0); 119 } 120 121 @Benchmark 122 public int changedElementQuad() { 123 return strlenInt(quadByteSegment, 0); 124 } 125 126 // These are the legacy methods 127 128 private static int legacy_strlen_byte(MemorySegment segment, long start) { 129 // iterate until overflow (String can only hold a byte[], whose length can be expressed as an int) 130 for (int offset = 0; offset >= 0; offset++) { 131 byte curr = segment.get(JAVA_BYTE, start + offset); 132 if (curr == 0) { 133 return offset; 134 } 135 } 136 throw new IllegalArgumentException("String too large"); 137 } 138 139 private static int legacy_strlen_short(MemorySegment segment, long start) { 140 // iterate until overflow (String can only hold a byte[], whose length can be expressed as an int) 141 for (int offset = 0; offset >= 0; offset += 2) { 142 short curr = segment.get(JAVA_SHORT, start + offset); 143 if (curr == 0) { 144 return offset; 145 } 146 } 147 throw new IllegalArgumentException("String too large"); 148 } 149 150 private static int legacy_strlen_int(MemorySegment segment, long start) { 151 // iterate until overflow (String can only hold a byte[], whose length can be expressed as an int) 152 for (int offset = 0; offset >= 0; offset += 4) { 153 int curr = segment.get(JAVA_INT, start + offset); 154 if (curr == 0) { 155 return offset; 156 } 157 } 158 throw new IllegalArgumentException("String too large"); 159 } 160 161 }