1 /*
2 * Copyright (c) 2020, 2025, 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
25 package org.openjdk.bench.java.lang.foreign;
26
27 import org.openjdk.jmh.annotations.Benchmark;
28 import org.openjdk.jmh.annotations.BenchmarkMode;
29 import org.openjdk.jmh.annotations.CompilerControl;
30 import org.openjdk.jmh.annotations.Fork;
31 import org.openjdk.jmh.annotations.Measurement;
32 import org.openjdk.jmh.annotations.Mode;
33 import org.openjdk.jmh.annotations.OutputTimeUnit;
34 import org.openjdk.jmh.annotations.Setup;
35 import org.openjdk.jmh.annotations.State;
36 import org.openjdk.jmh.annotations.TearDown;
37 import org.openjdk.jmh.annotations.Warmup;
38 import jdk.internal.misc.Unsafe;
39
40 import java.lang.foreign.Arena;
41 import java.lang.foreign.MemorySegment;
42 import java.nio.ByteBuffer;
43 import java.nio.IntBuffer;
44 import java.util.concurrent.TimeUnit;
45
46 import static java.lang.foreign.ValueLayout.JAVA_INT;
47 import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED;
48
49 @BenchmarkMode(Mode.AverageTime)
50 @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
51 @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
52 @State(org.openjdk.jmh.annotations.Scope.Thread)
53 @OutputTimeUnit(TimeUnit.MILLISECONDS)
54 @Fork(value = 3, jvmArgs = { "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" })
55 public class BulkOps {
56
57 static final Unsafe unsafe = Utils.unsafe;
58
59 static final int ELEM_SIZE = 1_000_000;
60 static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
61 static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
62
63 final Arena arena = Arena.ofShared();
64
65 final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE);
66 final MemorySegment segment = arena.allocate(ALLOC_SIZE, 1);
67
68 final IntBuffer buffer = IntBuffer.allocate(ELEM_SIZE);
69
70 final int[] ints = new int[ELEM_SIZE];
71 @SuppressWarnings("initialization")
72 final MemorySegment bytesSegment = MemorySegment.ofArray(ints);
73 final long UNSAFE_INT_OFFSET = unsafe.arrayBaseOffset(int[].class);
74
75 // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized
76 static final int SIZE_WITH_TAIL = (1024 * 1024) + 7;
77 final MemorySegment mismatchSegmentLarge1;
78
79 {
80 mismatchSegmentLarge1 = arena.allocate(SIZE_WITH_TAIL, 1);
81 }
82
83 final MemorySegment mismatchSegmentLarge2 = arena.allocate(SIZE_WITH_TAIL, 1);
84 final ByteBuffer mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL);
85 final ByteBuffer mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL);
86
87 // mismatch at first byte
88 final MemorySegment mismatchSegmentSmall1 = arena.allocate(7, 1);
89 final MemorySegment mismatchSegmentSmall2 = arena.allocate(7, 1);
90 final ByteBuffer mismatchBufferSmall1 = ByteBuffer.allocateDirect(7);
91 final ByteBuffer mismatchBufferSmall2 = ByteBuffer.allocateDirect(7);
92
93 @Setup
94 public void setup() {
95 mismatchSegmentSmall1.fill((byte) 0xFF);
96 mismatchBufferSmall1.put((byte) 0xFF).clear();
97 // verify expected mismatch indices
98 long si = mismatchSegmentLarge1.mismatch(mismatchSegmentLarge2);
99 if (si != -1)
100 throw new AssertionError("Unexpected mismatch index:" + si);
101 int bi = mismatchBufferLarge1.mismatch(mismatchBufferLarge2);
102 if (bi != -1)
103 throw new AssertionError("Unexpected mismatch index:" + bi);
104 si = mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2);
105 if (si != 0)
106 throw new AssertionError("Unexpected mismatch index:" + si);
107 bi = mismatchBufferSmall1.mismatch(mismatchBufferSmall2);
108 if (bi != 0)
109 throw new AssertionError("Unexpected mismatch index:" + bi);
110
111 for (int i = 0; i < ints.length ; i++) {
112 ints[i] = i;
113 }
114 }
115
116 @TearDown
117 public void tearDown() {
118 arena.close();
119 }
120
121 @Benchmark
122 @OutputTimeUnit(TimeUnit.NANOSECONDS)
123 public void unsafe_fill() {
124 unsafe.setMemory(unsafe_addr, ALLOC_SIZE, (byte)42);
125 }
126
127 @Benchmark
128 @OutputTimeUnit(TimeUnit.NANOSECONDS)
129 public void segment_fill() {
130 segment.fill((byte)42);
131 }
132
133 @Benchmark
134 @OutputTimeUnit(TimeUnit.NANOSECONDS)
135 public void unsafe_copy() {
136 unsafe.copyMemory(ints, UNSAFE_INT_OFFSET, null, unsafe_addr, ALLOC_SIZE);
137 }
138
139 @Benchmark
140 @OutputTimeUnit(TimeUnit.NANOSECONDS)
141 public void segment_copy() {
142 segment.copyFrom(bytesSegment);
143 }
144
145 @Benchmark
146 @OutputTimeUnit(TimeUnit.NANOSECONDS)
147 public void segment_copy_static() {
148 MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, ints.length);
149 }
150
151 @Benchmark
152 @OutputTimeUnit(TimeUnit.NANOSECONDS)
153 public void segment_copy_static_small() {
154 MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, 10);
155 }
156
157 @Benchmark
158 @CompilerControl(CompilerControl.Mode.DONT_INLINE)
159 @OutputTimeUnit(TimeUnit.NANOSECONDS)
160 public void segment_copy_static_small_dontinline() {
161 MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, 10);
162 }
163
164 @Benchmark
165 @OutputTimeUnit(TimeUnit.NANOSECONDS)
166 public void unsafe_copy_small() {
167 unsafe.copyMemory(ints, UNSAFE_INT_OFFSET, null, unsafe_addr, 10 * CARRIER_SIZE);
168 }
169
170 @Benchmark
171 @OutputTimeUnit(TimeUnit.NANOSECONDS)
172 public void buffer_copy_small() {
173 buffer.put(0, ints, 0, 10);
174 }
175
176 @Benchmark
177 @OutputTimeUnit(TimeUnit.NANOSECONDS)
178 public void buffer_copy() {
179 buffer.put(0, ints, 0, ints.length);
180 }
181
182 @Benchmark
183 @CompilerControl(CompilerControl.Mode.DONT_INLINE)
184 @OutputTimeUnit(TimeUnit.NANOSECONDS)
185 public void segment_copy_static_dontinline() {
186 MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, ints.length);
187 }
188
189 @Benchmark
190 @OutputTimeUnit(TimeUnit.NANOSECONDS)
191 public long mismatch_large_segment() {
192 return mismatchSegmentLarge1.mismatch(mismatchSegmentLarge2);
193 }
194
195 @Benchmark
196 @OutputTimeUnit(TimeUnit.NANOSECONDS)
197 public int mismatch_large_bytebuffer() {
198 return mismatchBufferLarge1.mismatch(mismatchBufferLarge2);
199 }
200
201 @Benchmark
202 @OutputTimeUnit(TimeUnit.NANOSECONDS)
203 public long mismatch_small_segment() {
204 return mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2);
205 }
206
207 @Benchmark
208 @OutputTimeUnit(TimeUnit.NANOSECONDS)
209 public int mismatch_small_bytebuffer() {
210 return mismatchBufferSmall1.mismatch(mismatchBufferSmall2);
211 }
212 }